1 //! This modules implements a function to resolve a path `foo::bar::baz` to a 2 //! def, which is used within the name resolution. 3 //! 4 //! When name resolution is finished, the result of resolving a path is either 5 //! `Some(def)` or `None`. However, when we are in process of resolving imports 6 //! or macros, there's a third possibility: 7 //! 8 //! I can't resolve this path right now, but I might be resolve this path 9 //! later, when more macros are expanded. 10 //! 11 //! `ReachedFixedPoint` signals about this. 12 13 use base_db::Edition; 14 use hir_expand::name::Name; 15 16 use crate::{ 17 db::DefDatabase, 18 item_scope::BUILTIN_SCOPE, 19 nameres::{sub_namespace_match, BuiltinShadowMode, DefMap, MacroSubNs}, 20 path::{ModPath, PathKind}, 21 per_ns::PerNs, 22 visibility::{RawVisibility, Visibility}, 23 AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, 24 }; 25 26 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 27 pub(super) enum ResolveMode { 28 Import, 29 Other, 30 } 31 32 #[derive(Debug, Clone, Copy, PartialEq, Eq)] 33 pub(super) enum ReachedFixedPoint { 34 Yes, 35 No, 36 } 37 38 #[derive(Debug, Clone)] 39 pub(super) struct ResolvePathResult { 40 pub(super) resolved_def: PerNs, 41 pub(super) segment_index: Option<usize>, 42 pub(super) reached_fixedpoint: ReachedFixedPoint, 43 pub(super) krate: Option<CrateId>, 44 } 45 46 impl ResolvePathResult { empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult47 fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult { 48 ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None) 49 } 50 with( resolved_def: PerNs, reached_fixedpoint: ReachedFixedPoint, segment_index: Option<usize>, krate: Option<CrateId>, ) -> ResolvePathResult51 fn with( 52 resolved_def: PerNs, 53 reached_fixedpoint: ReachedFixedPoint, 54 segment_index: Option<usize>, 55 krate: Option<CrateId>, 56 ) -> ResolvePathResult { 57 ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, krate } 58 } 59 } 60 61 impl PerNs { filter_macro( mut self, db: &dyn DefDatabase, expected: Option<MacroSubNs>, ) -> Self62 pub(super) fn filter_macro( 63 mut self, 64 db: &dyn DefDatabase, 65 expected: Option<MacroSubNs>, 66 ) -> Self { 67 self.macros = self.macros.filter(|&(id, _)| { 68 let this = MacroSubNs::from_id(db, id); 69 sub_namespace_match(Some(this), expected) 70 }); 71 72 self 73 } 74 } 75 76 impl DefMap { resolve_visibility( &self, db: &dyn DefDatabase, original_module: LocalModuleId, visibility: &RawVisibility, within_impl: bool, ) -> Option<Visibility>77 pub(crate) fn resolve_visibility( 78 &self, 79 db: &dyn DefDatabase, 80 // module to import to 81 original_module: LocalModuleId, 82 // pub(path) 83 // ^^^^ this 84 visibility: &RawVisibility, 85 within_impl: bool, 86 ) -> Option<Visibility> { 87 let mut vis = match visibility { 88 RawVisibility::Module(path) => { 89 let (result, remaining) = 90 self.resolve_path(db, original_module, path, BuiltinShadowMode::Module, None); 91 if remaining.is_some() { 92 return None; 93 } 94 let types = result.take_types()?; 95 match types { 96 ModuleDefId::ModuleId(m) => Visibility::Module(m), 97 _ => { 98 // error: visibility needs to refer to module 99 return None; 100 } 101 } 102 } 103 RawVisibility::Public => Visibility::Public, 104 }; 105 106 // In block expressions, `self` normally refers to the containing non-block module, and 107 // `super` to its parent (etc.). However, visibilities must only refer to a module in the 108 // DefMap they're written in, so we restrict them when that happens. 109 if let Visibility::Module(m) = vis { 110 // ...unless we're resolving visibility for an associated item in an impl. 111 if self.block_id() != m.block && !within_impl { 112 cov_mark::hit!(adjust_vis_in_block_def_map); 113 vis = Visibility::Module(self.module_id(Self::ROOT)); 114 tracing::debug!("visibility {:?} points outside DefMap, adjusting to {:?}", m, vis); 115 } 116 } 117 118 Some(vis) 119 } 120 121 // Returns Yes if we are sure that additions to `ItemMap` wouldn't change 122 // the result. resolve_path_fp_with_macro( &self, db: &dyn DefDatabase, mode: ResolveMode, mut original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> ResolvePathResult123 pub(super) fn resolve_path_fp_with_macro( 124 &self, 125 db: &dyn DefDatabase, 126 mode: ResolveMode, 127 // module to import to 128 mut original_module: LocalModuleId, 129 path: &ModPath, 130 shadow: BuiltinShadowMode, 131 // Pass `MacroSubNs` if we know we're resolving macro names and which kind of macro we're 132 // resolving them to. Pass `None` otherwise, e.g. when we're resolving import paths. 133 expected_macro_subns: Option<MacroSubNs>, 134 ) -> ResolvePathResult { 135 let mut result = ResolvePathResult::empty(ReachedFixedPoint::No); 136 137 let mut arc; 138 let mut current_map = self; 139 loop { 140 let new = current_map.resolve_path_fp_with_macro_single( 141 db, 142 mode, 143 original_module, 144 path, 145 shadow, 146 expected_macro_subns, 147 ); 148 149 // Merge `new` into `result`. 150 result.resolved_def = result.resolved_def.or(new.resolved_def); 151 if result.reached_fixedpoint == ReachedFixedPoint::No { 152 result.reached_fixedpoint = new.reached_fixedpoint; 153 } 154 // FIXME: this doesn't seem right; what if the different namespace resolutions come from different crates? 155 result.krate = result.krate.or(new.krate); 156 result.segment_index = match (result.segment_index, new.segment_index) { 157 (Some(idx), None) => Some(idx), 158 (Some(old), Some(new)) => Some(old.max(new)), 159 (None, new) => new, 160 }; 161 162 match ¤t_map.block { 163 Some(block) => { 164 original_module = block.parent.local_id; 165 arc = block.parent.def_map(db, current_map.krate); 166 current_map = &*arc; 167 } 168 None => return result, 169 } 170 } 171 } 172 resolve_path_fp_with_macro_single( &self, db: &dyn DefDatabase, mode: ResolveMode, original_module: LocalModuleId, path: &ModPath, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> ResolvePathResult173 pub(super) fn resolve_path_fp_with_macro_single( 174 &self, 175 db: &dyn DefDatabase, 176 mode: ResolveMode, 177 original_module: LocalModuleId, 178 path: &ModPath, 179 shadow: BuiltinShadowMode, 180 expected_macro_subns: Option<MacroSubNs>, 181 ) -> ResolvePathResult { 182 let graph = db.crate_graph(); 183 let _cx = stdx::panic_context::enter(format!( 184 "DefMap {:?} crate_name={:?} block={:?} path={}", 185 self.krate, 186 graph[self.krate].display_name, 187 self.block, 188 path.display(db.upcast()) 189 )); 190 191 let mut segments = path.segments().iter().enumerate(); 192 let mut curr_per_ns: PerNs = match path.kind { 193 PathKind::DollarCrate(krate) => { 194 if krate == self.krate { 195 cov_mark::hit!(macro_dollar_crate_self); 196 PerNs::types(self.crate_root().into(), Visibility::Public) 197 } else { 198 let def_map = db.crate_def_map(krate); 199 let module = def_map.module_id(Self::ROOT); 200 cov_mark::hit!(macro_dollar_crate_other); 201 PerNs::types(module.into(), Visibility::Public) 202 } 203 } 204 PathKind::Crate => PerNs::types(self.crate_root().into(), Visibility::Public), 205 // plain import or absolute path in 2015: crate-relative with 206 // fallback to extern prelude (with the simplification in 207 // rust-lang/rust#57745) 208 // FIXME there must be a nicer way to write this condition 209 PathKind::Plain | PathKind::Abs 210 if self.data.edition == Edition::Edition2015 211 && (path.kind == PathKind::Abs || mode == ResolveMode::Import) => 212 { 213 let (_, segment) = match segments.next() { 214 Some((idx, segment)) => (idx, segment), 215 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 216 }; 217 tracing::debug!("resolving {:?} in crate root (+ extern prelude)", segment); 218 self.resolve_name_in_crate_root_or_extern_prelude(db, segment) 219 } 220 PathKind::Plain => { 221 let (_, segment) = match segments.next() { 222 Some((idx, segment)) => (idx, segment), 223 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 224 }; 225 // The first segment may be a builtin type. If the path has more 226 // than one segment, we first try resolving it as a module 227 // anyway. 228 // FIXME: If the next segment doesn't resolve in the module and 229 // BuiltinShadowMode wasn't Module, then we need to try 230 // resolving it as a builtin. 231 let prefer_module = 232 if path.segments().len() == 1 { shadow } else { BuiltinShadowMode::Module }; 233 234 tracing::debug!("resolving {:?} in module", segment); 235 self.resolve_name_in_module( 236 db, 237 original_module, 238 segment, 239 prefer_module, 240 expected_macro_subns, 241 ) 242 } 243 PathKind::Super(lvl) => { 244 let mut module = original_module; 245 for i in 0..lvl { 246 match self.modules[module].parent { 247 Some(it) => module = it, 248 None => match &self.block { 249 Some(block) => { 250 // Look up remaining path in parent `DefMap` 251 let new_path = ModPath::from_segments( 252 PathKind::Super(lvl - i), 253 path.segments().to_vec(), 254 ); 255 tracing::debug!( 256 "`super` path: {} -> {} in parent map", 257 path.display(db.upcast()), 258 new_path.display(db.upcast()) 259 ); 260 return block 261 .parent 262 .def_map(db, self.krate) 263 .resolve_path_fp_with_macro( 264 db, 265 mode, 266 block.parent.local_id, 267 &new_path, 268 shadow, 269 expected_macro_subns, 270 ); 271 } 272 None => { 273 tracing::debug!("super path in root module"); 274 return ResolvePathResult::empty(ReachedFixedPoint::Yes); 275 } 276 }, 277 } 278 } 279 280 // Resolve `self` to the containing crate-rooted module if we're a block 281 self.with_ancestor_maps(db, module, &mut |def_map, module| { 282 if def_map.block.is_some() { 283 None // keep ascending 284 } else { 285 Some(PerNs::types(def_map.module_id(module).into(), Visibility::Public)) 286 } 287 }) 288 .expect("block DefMap not rooted in crate DefMap") 289 } 290 PathKind::Abs => { 291 // 2018-style absolute path -- only extern prelude 292 let segment = match segments.next() { 293 Some((_, segment)) => segment, 294 None => return ResolvePathResult::empty(ReachedFixedPoint::Yes), 295 }; 296 if let Some(&def) = self.data.extern_prelude.get(segment) { 297 tracing::debug!("absolute path {:?} resolved to crate {:?}", path, def); 298 PerNs::types(def.into(), Visibility::Public) 299 } else { 300 return ResolvePathResult::empty(ReachedFixedPoint::No); // extern crate declarations can add to the extern prelude 301 } 302 } 303 }; 304 305 for (i, segment) in segments { 306 let (curr, vis) = match curr_per_ns.take_types_vis() { 307 Some(r) => r, 308 None => { 309 // we still have path segments left, but the path so far 310 // didn't resolve in the types namespace => no resolution 311 // (don't break here because `curr_per_ns` might contain 312 // something in the value namespace, and it would be wrong 313 // to return that) 314 return ResolvePathResult::empty(ReachedFixedPoint::No); 315 } 316 }; 317 // resolve segment in curr 318 319 curr_per_ns = match curr { 320 ModuleDefId::ModuleId(module) => { 321 if module.krate != self.krate { 322 let path = ModPath::from_segments( 323 PathKind::Super(0), 324 path.segments()[i..].iter().cloned(), 325 ); 326 tracing::debug!("resolving {:?} in other crate", path); 327 let defp_map = module.def_map(db); 328 // Macro sub-namespaces only matter when resolving single-segment paths 329 // because `macro_use` and other preludes should be taken into account. At 330 // this point, we know we're resolving a multi-segment path so macro kind 331 // expectation is discarded. 332 let (def, s) = 333 defp_map.resolve_path(db, module.local_id, &path, shadow, None); 334 return ResolvePathResult::with( 335 def, 336 ReachedFixedPoint::Yes, 337 s.map(|s| s + i), 338 Some(module.krate), 339 ); 340 } 341 342 let def_map; 343 let module_data = if module.block == self.block_id() { 344 &self[module.local_id] 345 } else { 346 def_map = module.def_map(db); 347 &def_map[module.local_id] 348 }; 349 350 // Since it is a qualified path here, it should not contains legacy macros 351 module_data.scope.get(segment) 352 } 353 ModuleDefId::AdtId(AdtId::EnumId(e)) => { 354 // enum variant 355 cov_mark::hit!(can_import_enum_variant); 356 let enum_data = db.enum_data(e); 357 match enum_data.variant(segment) { 358 Some(local_id) => { 359 let variant = EnumVariantId { parent: e, local_id }; 360 match &*enum_data.variants[local_id].variant_data { 361 crate::data::adt::VariantData::Record(_) => { 362 PerNs::types(variant.into(), Visibility::Public) 363 } 364 crate::data::adt::VariantData::Tuple(_) 365 | crate::data::adt::VariantData::Unit => { 366 PerNs::both(variant.into(), variant.into(), Visibility::Public) 367 } 368 } 369 } 370 None => { 371 return ResolvePathResult::with( 372 PerNs::types(e.into(), vis), 373 ReachedFixedPoint::Yes, 374 Some(i), 375 Some(self.krate), 376 ); 377 } 378 } 379 } 380 s => { 381 // could be an inherent method call in UFCS form 382 // (`Struct::method`), or some other kind of associated item 383 tracing::debug!( 384 "path segment {:?} resolved to non-module {:?}, but is not last", 385 segment, 386 curr, 387 ); 388 389 return ResolvePathResult::with( 390 PerNs::types(s, vis), 391 ReachedFixedPoint::Yes, 392 Some(i), 393 Some(self.krate), 394 ); 395 } 396 }; 397 398 curr_per_ns = curr_per_ns 399 .filter_visibility(|vis| vis.is_visible_from_def_map(db, self, original_module)); 400 } 401 402 ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate)) 403 } 404 resolve_name_in_module( &self, db: &dyn DefDatabase, module: LocalModuleId, name: &Name, shadow: BuiltinShadowMode, expected_macro_subns: Option<MacroSubNs>, ) -> PerNs405 fn resolve_name_in_module( 406 &self, 407 db: &dyn DefDatabase, 408 module: LocalModuleId, 409 name: &Name, 410 shadow: BuiltinShadowMode, 411 expected_macro_subns: Option<MacroSubNs>, 412 ) -> PerNs { 413 // Resolve in: 414 // - legacy scope of macro 415 // - current module / scope 416 // - extern prelude / macro_use prelude 417 // - std prelude 418 let from_legacy_macro = self[module] 419 .scope 420 .get_legacy_macro(name) 421 // FIXME: shadowing 422 .and_then(|it| it.last()) 423 .copied() 424 .filter(|&id| { 425 sub_namespace_match(Some(MacroSubNs::from_id(db, id)), expected_macro_subns) 426 }) 427 .map_or_else(PerNs::none, |m| PerNs::macros(m, Visibility::Public)); 428 let from_scope = self[module].scope.get(name).filter_macro(db, expected_macro_subns); 429 let from_builtin = match self.block { 430 Some(_) => { 431 // Only resolve to builtins in the root `DefMap`. 432 PerNs::none() 433 } 434 None => BUILTIN_SCOPE.get(name).copied().unwrap_or_else(PerNs::none), 435 }; 436 let from_scope_or_builtin = match shadow { 437 BuiltinShadowMode::Module => from_scope.or(from_builtin), 438 BuiltinShadowMode::Other => match from_scope.take_types() { 439 Some(ModuleDefId::ModuleId(_)) => from_builtin.or(from_scope), 440 Some(_) | None => from_scope.or(from_builtin), 441 }, 442 }; 443 444 let extern_prelude = || { 445 if self.block.is_some() { 446 // Don't resolve extern prelude in block `DefMap`s. 447 return PerNs::none(); 448 } 449 self.data 450 .extern_prelude 451 .get(name) 452 .map_or(PerNs::none(), |&it| PerNs::types(it.into(), Visibility::Public)) 453 }; 454 let macro_use_prelude = || { 455 self.macro_use_prelude 456 .get(name) 457 .map_or(PerNs::none(), |&it| PerNs::macros(it.into(), Visibility::Public)) 458 }; 459 let prelude = || self.resolve_in_prelude(db, name); 460 461 from_legacy_macro 462 .or(from_scope_or_builtin) 463 .or_else(extern_prelude) 464 .or_else(macro_use_prelude) 465 .or_else(prelude) 466 } 467 resolve_name_in_crate_root_or_extern_prelude( &self, db: &dyn DefDatabase, name: &Name, ) -> PerNs468 fn resolve_name_in_crate_root_or_extern_prelude( 469 &self, 470 db: &dyn DefDatabase, 471 name: &Name, 472 ) -> PerNs { 473 let from_crate_root = match self.block { 474 Some(_) => { 475 let def_map = self.crate_root().def_map(db); 476 def_map[Self::ROOT].scope.get(name) 477 } 478 None => self[Self::ROOT].scope.get(name), 479 }; 480 let from_extern_prelude = || { 481 if self.block.is_some() { 482 // Don't resolve extern prelude in block `DefMap`s. 483 return PerNs::none(); 484 } 485 self.data 486 .extern_prelude 487 .get(name) 488 .copied() 489 .map_or(PerNs::none(), |it| PerNs::types(it.into(), Visibility::Public)) 490 }; 491 492 from_crate_root.or_else(from_extern_prelude) 493 } 494 resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs495 fn resolve_in_prelude(&self, db: &dyn DefDatabase, name: &Name) -> PerNs { 496 if let Some(prelude) = self.prelude { 497 let keep; 498 let def_map = if prelude.krate == self.krate { 499 self 500 } else { 501 // Extend lifetime 502 keep = prelude.def_map(db); 503 &keep 504 }; 505 def_map[prelude.local_id].scope.get(name) 506 } else { 507 PerNs::none() 508 } 509 } 510 } 511