1 //! Defines `Body`: a lowered representation of bodies of functions, statics and 2 //! consts. 3 mod lower; 4 #[cfg(test)] 5 mod tests; 6 pub mod scope; 7 8 use std::{mem, ops::Index, sync::Arc}; 9 10 use base_db::CrateId; 11 use cfg::{CfgExpr, CfgOptions}; 12 use drop_bomb::DropBomb; 13 use either::Either; 14 use hir_expand::{ 15 ast_id_map::AstIdMap, hygiene::Hygiene, AstId, ExpandResult, HirFileId, InFile, MacroDefId, 16 }; 17 use la_arena::{Arena, ArenaMap}; 18 use limit::Limit; 19 use profile::Count; 20 use rustc_hash::FxHashMap; 21 use syntax::{ast, AstNode, AstPtr, SyntaxNodePtr}; 22 23 use crate::{ 24 attr::{Attrs, RawAttrs}, 25 db::DefDatabase, 26 expr::{Expr, ExprId, Label, LabelId, Pat, PatId}, 27 item_scope::BuiltinShadowMode, 28 nameres::DefMap, 29 path::{ModPath, Path}, 30 src::HasSource, 31 AsMacroCall, BlockId, DefWithBodyId, HasModule, LocalModuleId, Lookup, ModuleId, 32 UnresolvedMacro, 33 }; 34 35 pub use lower::LowerCtx; 36 37 /// A subset of Expander that only deals with cfg attributes. We only need it to 38 /// avoid cyclic queries in crate def map during enum processing. 39 #[derive(Debug)] 40 pub(crate) struct CfgExpander { 41 cfg_options: CfgOptions, 42 hygiene: Hygiene, 43 krate: CrateId, 44 } 45 46 #[derive(Debug)] 47 pub struct Expander { 48 cfg_expander: CfgExpander, 49 def_map: Arc<DefMap>, 50 current_file_id: HirFileId, 51 ast_id_map: Arc<AstIdMap>, 52 module: LocalModuleId, 53 recursion_limit: usize, 54 } 55 56 #[cfg(test)] 57 static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(32); 58 59 #[cfg(not(test))] 60 static EXPANSION_RECURSION_LIMIT: Limit = Limit::new(128); 61 62 impl CfgExpander { new( db: &dyn DefDatabase, current_file_id: HirFileId, krate: CrateId, ) -> CfgExpander63 pub(crate) fn new( 64 db: &dyn DefDatabase, 65 current_file_id: HirFileId, 66 krate: CrateId, 67 ) -> CfgExpander { 68 let hygiene = Hygiene::new(db.upcast(), current_file_id); 69 let cfg_options = db.crate_graph()[krate].cfg_options.clone(); 70 CfgExpander { cfg_options, hygiene, krate } 71 } 72 parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs73 pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { 74 RawAttrs::new(db, owner, &self.hygiene).filter(db, self.krate) 75 } 76 is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool77 pub(crate) fn is_cfg_enabled(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> bool { 78 let attrs = self.parse_attrs(db, owner); 79 attrs.is_cfg_enabled(&self.cfg_options) 80 } 81 } 82 83 impl Expander { new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander84 pub fn new(db: &dyn DefDatabase, current_file_id: HirFileId, module: ModuleId) -> Expander { 85 let cfg_expander = CfgExpander::new(db, current_file_id, module.krate); 86 let def_map = module.def_map(db); 87 let ast_id_map = db.ast_id_map(current_file_id); 88 Expander { 89 cfg_expander, 90 def_map, 91 current_file_id, 92 ast_id_map, 93 module: module.local_id, 94 recursion_limit: 0, 95 } 96 } 97 enter_expand<T: ast::AstNode>( &mut self, db: &dyn DefDatabase, macro_call: ast::MacroCall, ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro>98 pub fn enter_expand<T: ast::AstNode>( 99 &mut self, 100 db: &dyn DefDatabase, 101 macro_call: ast::MacroCall, 102 ) -> Result<ExpandResult<Option<(Mark, T)>>, UnresolvedMacro> { 103 if EXPANSION_RECURSION_LIMIT.check(self.recursion_limit + 1).is_err() { 104 cov_mark::hit!(your_stack_belongs_to_me); 105 return Ok(ExpandResult::str_err( 106 "reached recursion limit during macro expansion".into(), 107 )); 108 } 109 110 let macro_call = InFile::new(self.current_file_id, ¯o_call); 111 112 let resolver = 113 |path: ModPath| -> Option<MacroDefId> { self.resolve_path_as_macro(db, &path) }; 114 115 let mut err = None; 116 let call_id = 117 macro_call.as_call_id_with_errors(db, self.def_map.krate(), resolver, &mut |e| { 118 err.get_or_insert(e); 119 })?; 120 let call_id = match call_id { 121 Ok(it) => it, 122 Err(_) => { 123 return Ok(ExpandResult { value: None, err }); 124 } 125 }; 126 127 if err.is_none() { 128 err = db.macro_expand_error(call_id); 129 } 130 131 let file_id = call_id.as_file(); 132 133 let raw_node = match db.parse_or_expand(file_id) { 134 Some(it) => it, 135 None => { 136 // Only `None` if the macro expansion produced no usable AST. 137 if err.is_none() { 138 tracing::warn!("no error despite `parse_or_expand` failing"); 139 } 140 141 return Ok(ExpandResult::only_err(err.unwrap_or_else(|| { 142 mbe::ExpandError::Other("failed to parse macro invocation".into()) 143 }))); 144 } 145 }; 146 147 let node = match T::cast(raw_node) { 148 Some(it) => it, 149 None => { 150 // This can happen without being an error, so only forward previous errors. 151 return Ok(ExpandResult { value: None, err }); 152 } 153 }; 154 155 tracing::debug!("macro expansion {:#?}", node.syntax()); 156 157 self.recursion_limit += 1; 158 let mark = Mark { 159 file_id: self.current_file_id, 160 ast_id_map: mem::take(&mut self.ast_id_map), 161 bomb: DropBomb::new("expansion mark dropped"), 162 }; 163 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), file_id); 164 self.current_file_id = file_id; 165 self.ast_id_map = db.ast_id_map(file_id); 166 167 Ok(ExpandResult { value: Some((mark, node)), err }) 168 } 169 exit(&mut self, db: &dyn DefDatabase, mut mark: Mark)170 pub fn exit(&mut self, db: &dyn DefDatabase, mut mark: Mark) { 171 self.cfg_expander.hygiene = Hygiene::new(db.upcast(), mark.file_id); 172 self.current_file_id = mark.file_id; 173 self.ast_id_map = mem::take(&mut mark.ast_id_map); 174 self.recursion_limit -= 1; 175 mark.bomb.defuse(); 176 } 177 to_source<T>(&self, value: T) -> InFile<T>178 pub(crate) fn to_source<T>(&self, value: T) -> InFile<T> { 179 InFile { file_id: self.current_file_id, value } 180 } 181 parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs182 pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs { 183 self.cfg_expander.parse_attrs(db, owner) 184 } 185 cfg_options(&self) -> &CfgOptions186 pub(crate) fn cfg_options(&self) -> &CfgOptions { 187 &self.cfg_expander.cfg_options 188 } 189 current_file_id(&self) -> HirFileId190 pub fn current_file_id(&self) -> HirFileId { 191 self.current_file_id 192 } 193 parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path>194 fn parse_path(&mut self, db: &dyn DefDatabase, path: ast::Path) -> Option<Path> { 195 let ctx = LowerCtx::with_hygiene(db, &self.cfg_expander.hygiene); 196 Path::from_src(path, &ctx) 197 } 198 resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId>199 fn resolve_path_as_macro(&self, db: &dyn DefDatabase, path: &ModPath) -> Option<MacroDefId> { 200 self.def_map.resolve_path(db, self.module, path, BuiltinShadowMode::Other).0.take_macros() 201 } 202 ast_id<N: AstNode>(&self, item: &N) -> AstId<N>203 fn ast_id<N: AstNode>(&self, item: &N) -> AstId<N> { 204 let file_local_id = self.ast_id_map.ast_id(item); 205 AstId::new(self.current_file_id, file_local_id) 206 } 207 } 208 209 #[derive(Debug)] 210 pub struct Mark { 211 file_id: HirFileId, 212 ast_id_map: Arc<AstIdMap>, 213 bomb: DropBomb, 214 } 215 216 /// The body of an item (function, const etc.). 217 #[derive(Debug, Eq, PartialEq)] 218 pub struct Body { 219 pub exprs: Arena<Expr>, 220 pub pats: Arena<Pat>, 221 pub labels: Arena<Label>, 222 /// The patterns for the function's parameters. While the parameter types are 223 /// part of the function signature, the patterns are not (they don't change 224 /// the external type of the function). 225 /// 226 /// If this `Body` is for the body of a constant, this will just be 227 /// empty. 228 pub params: Vec<PatId>, 229 /// The `ExprId` of the actual body expression. 230 pub body_expr: ExprId, 231 /// Block expressions in this body that may contain inner items. 232 block_scopes: Vec<BlockId>, 233 _c: Count<Self>, 234 } 235 236 pub type ExprPtr = AstPtr<ast::Expr>; 237 pub type ExprSource = InFile<ExprPtr>; 238 239 pub type PatPtr = Either<AstPtr<ast::Pat>, AstPtr<ast::SelfParam>>; 240 pub type PatSource = InFile<PatPtr>; 241 242 pub type LabelPtr = AstPtr<ast::Label>; 243 pub type LabelSource = InFile<LabelPtr>; 244 /// An item body together with the mapping from syntax nodes to HIR expression 245 /// IDs. This is needed to go from e.g. a position in a file to the HIR 246 /// expression containing it; but for type inference etc., we want to operate on 247 /// a structure that is agnostic to the actual positions of expressions in the 248 /// file, so that we don't recompute types whenever some whitespace is typed. 249 /// 250 /// One complication here is that, due to macro expansion, a single `Body` might 251 /// be spread across several files. So, for each ExprId and PatId, we record 252 /// both the HirFileId and the position inside the file. However, we only store 253 /// AST -> ExprId mapping for non-macro files, as it is not clear how to handle 254 /// this properly for macros. 255 #[derive(Default, Debug, Eq, PartialEq)] 256 pub struct BodySourceMap { 257 expr_map: FxHashMap<ExprSource, ExprId>, 258 expr_map_back: ArenaMap<ExprId, Result<ExprSource, SyntheticSyntax>>, 259 260 pat_map: FxHashMap<PatSource, PatId>, 261 pat_map_back: ArenaMap<PatId, Result<PatSource, SyntheticSyntax>>, 262 263 label_map: FxHashMap<LabelSource, LabelId>, 264 label_map_back: ArenaMap<LabelId, LabelSource>, 265 266 /// We don't create explicit nodes for record fields (`S { record_field: 92 }`). 267 /// Instead, we use id of expression (`92`) to identify the field. 268 field_map: FxHashMap<InFile<AstPtr<ast::RecordExprField>>, ExprId>, 269 field_map_back: FxHashMap<ExprId, InFile<AstPtr<ast::RecordExprField>>>, 270 271 expansions: FxHashMap<InFile<AstPtr<ast::MacroCall>>, HirFileId>, 272 273 /// Diagnostics accumulated during body lowering. These contain `AstPtr`s and so are stored in 274 /// the source map (since they're just as volatile). 275 diagnostics: Vec<BodyDiagnostic>, 276 } 277 278 #[derive(Default, Debug, Eq, PartialEq, Clone, Copy)] 279 pub struct SyntheticSyntax; 280 281 #[derive(Debug, Eq, PartialEq)] 282 pub enum BodyDiagnostic { 283 InactiveCode { node: InFile<SyntaxNodePtr>, cfg: CfgExpr, opts: CfgOptions }, 284 MacroError { node: InFile<AstPtr<ast::MacroCall>>, message: String }, 285 UnresolvedProcMacro { node: InFile<AstPtr<ast::MacroCall>> }, 286 UnresolvedMacroCall { node: InFile<AstPtr<ast::MacroCall>>, path: ModPath }, 287 } 288 289 impl Body { body_with_source_map_query( db: &dyn DefDatabase, def: DefWithBodyId, ) -> (Arc<Body>, Arc<BodySourceMap>)290 pub(crate) fn body_with_source_map_query( 291 db: &dyn DefDatabase, 292 def: DefWithBodyId, 293 ) -> (Arc<Body>, Arc<BodySourceMap>) { 294 let _p = profile::span("body_with_source_map_query"); 295 let mut params = None; 296 297 let (file_id, module, body) = match def { 298 DefWithBodyId::FunctionId(f) => { 299 let f = f.lookup(db); 300 let src = f.source(db); 301 params = src.value.param_list(); 302 (src.file_id, f.module(db), src.value.body().map(ast::Expr::from)) 303 } 304 DefWithBodyId::ConstId(c) => { 305 let c = c.lookup(db); 306 let src = c.source(db); 307 (src.file_id, c.module(db), src.value.body()) 308 } 309 DefWithBodyId::StaticId(s) => { 310 let s = s.lookup(db); 311 let src = s.source(db); 312 (src.file_id, s.module(db), src.value.body()) 313 } 314 }; 315 let expander = Expander::new(db, file_id, module); 316 let (mut body, source_map) = Body::new(db, expander, params, body); 317 body.shrink_to_fit(); 318 (Arc::new(body), Arc::new(source_map)) 319 } 320 body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body>321 pub(crate) fn body_query(db: &dyn DefDatabase, def: DefWithBodyId) -> Arc<Body> { 322 db.body_with_source_map(def).0 323 } 324 325 /// Returns an iterator over all block expressions in this body that define inner items. blocks<'a>( &'a self, db: &'a dyn DefDatabase, ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_326 pub fn blocks<'a>( 327 &'a self, 328 db: &'a dyn DefDatabase, 329 ) -> impl Iterator<Item = (BlockId, Arc<DefMap>)> + '_ { 330 self.block_scopes 331 .iter() 332 .map(move |block| (*block, db.block_def_map(*block).expect("block ID without DefMap"))) 333 } 334 new( db: &dyn DefDatabase, expander: Expander, params: Option<ast::ParamList>, body: Option<ast::Expr>, ) -> (Body, BodySourceMap)335 fn new( 336 db: &dyn DefDatabase, 337 expander: Expander, 338 params: Option<ast::ParamList>, 339 body: Option<ast::Expr>, 340 ) -> (Body, BodySourceMap) { 341 lower::lower(db, expander, params, body) 342 } 343 shrink_to_fit(&mut self)344 fn shrink_to_fit(&mut self) { 345 let Self { _c: _, body_expr: _, block_scopes, exprs, labels, params, pats } = self; 346 block_scopes.shrink_to_fit(); 347 exprs.shrink_to_fit(); 348 labels.shrink_to_fit(); 349 params.shrink_to_fit(); 350 pats.shrink_to_fit(); 351 } 352 } 353 354 impl Index<ExprId> for Body { 355 type Output = Expr; 356 index(&self, expr: ExprId) -> &Expr357 fn index(&self, expr: ExprId) -> &Expr { 358 &self.exprs[expr] 359 } 360 } 361 362 impl Index<PatId> for Body { 363 type Output = Pat; 364 index(&self, pat: PatId) -> &Pat365 fn index(&self, pat: PatId) -> &Pat { 366 &self.pats[pat] 367 } 368 } 369 370 impl Index<LabelId> for Body { 371 type Output = Label; 372 index(&self, label: LabelId) -> &Label373 fn index(&self, label: LabelId) -> &Label { 374 &self.labels[label] 375 } 376 } 377 378 // FIXME: Change `node_` prefix to something more reasonable. 379 // Perhaps `expr_syntax` and `expr_id`? 380 impl BodySourceMap { expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax>381 pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprSource, SyntheticSyntax> { 382 self.expr_map_back[expr].clone() 383 } 384 node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId>385 pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprId> { 386 let src = node.map(|it| AstPtr::new(it)); 387 self.expr_map.get(&src).cloned() 388 } 389 node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId>390 pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<HirFileId> { 391 let src = node.map(|it| AstPtr::new(it)); 392 self.expansions.get(&src).cloned() 393 } 394 pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax>395 pub fn pat_syntax(&self, pat: PatId) -> Result<PatSource, SyntheticSyntax> { 396 self.pat_map_back[pat].clone() 397 } 398 node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId>399 pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<PatId> { 400 let src = node.map(|it| Either::Left(AstPtr::new(it))); 401 self.pat_map.get(&src).cloned() 402 } 403 node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId>404 pub fn node_self_param(&self, node: InFile<&ast::SelfParam>) -> Option<PatId> { 405 let src = node.map(|it| Either::Right(AstPtr::new(it))); 406 self.pat_map.get(&src).cloned() 407 } 408 label_syntax(&self, label: LabelId) -> LabelSource409 pub fn label_syntax(&self, label: LabelId) -> LabelSource { 410 self.label_map_back[label].clone() 411 } 412 node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId>413 pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> { 414 let src = node.map(|it| AstPtr::new(it)); 415 self.label_map.get(&src).cloned() 416 } 417 field_syntax(&self, expr: ExprId) -> InFile<AstPtr<ast::RecordExprField>>418 pub fn field_syntax(&self, expr: ExprId) -> InFile<AstPtr<ast::RecordExprField>> { 419 self.field_map_back[&expr].clone() 420 } node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId>421 pub fn node_field(&self, node: InFile<&ast::RecordExprField>) -> Option<ExprId> { 422 let src = node.map(|it| AstPtr::new(it)); 423 self.field_map.get(&src).cloned() 424 } 425 426 /// Get a reference to the body source map's diagnostics. diagnostics(&self) -> &[BodyDiagnostic]427 pub fn diagnostics(&self) -> &[BodyDiagnostic] { 428 &self.diagnostics 429 } 430 } 431