1386: Remove one of the two different algorithms for name resolution of macros :D r=edwin0cheng a=matklad

r? @edwin0cheng 

Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
bors[bot] 2019-06-08 18:41:35 +00:00
commit 9c92c05ca6
9 changed files with 97 additions and 88 deletions

View file

@ -827,25 +827,25 @@ where
ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr), ast::ExprKind::RangeExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::ExprKind::MacroCall(e) => { ast::ExprKind::MacroCall(e) => {
// very hacky.FIXME change to use the macro resolution
let path = e.path().and_then(Path::from_ast);
let ast_id = self let ast_id = self
.db .db
.ast_id_map(self.current_file_id) .ast_id_map(self.current_file_id)
.ast_id(e) .ast_id(e)
.with_file_id(self.current_file_id); .with_file_id(self.current_file_id);
if let Some(def) = self.resolver.resolve_macro_call(self.db, path) { if let Some(path) = e.path().and_then(Path::from_ast) {
let call_id = MacroCallLoc { def, ast_id }.id(self.db); if let Some(def) = self.resolver.resolve_path_as_macro(self.db, &path) {
let file_id = call_id.as_file(MacroFileKind::Expr); let call_id = MacroCallLoc { def: def.id, ast_id }.id(self.db);
if let Some(node) = self.db.parse_or_expand(file_id) { let file_id = call_id.as_file(MacroFileKind::Expr);
if let Some(expr) = ast::Expr::cast(&*node) { if let Some(node) = self.db.parse_or_expand(file_id) {
log::debug!("macro expansion {}", expr.syntax().debug_dump()); if let Some(expr) = ast::Expr::cast(&*node) {
let old_file_id = std::mem::replace(&mut self.current_file_id, file_id); log::debug!("macro expansion {}", expr.syntax().debug_dump());
let id = self.collect_expr(&expr); let old_file_id =
self.current_file_id = old_file_id; std::mem::replace(&mut self.current_file_id, file_id);
return id; let id = self.collect_expr(&expr);
self.current_file_id = old_file_id;
return id;
}
} }
} }
} }

View file

@ -92,7 +92,6 @@ pub struct CrateDefMap {
extern_prelude: FxHashMap<Name, ModuleDef>, extern_prelude: FxHashMap<Name, ModuleDef>,
root: CrateModuleId, root: CrateModuleId,
modules: Arena<CrateModuleId, ModuleData>, modules: Arena<CrateModuleId, ModuleData>,
public_macros: FxHashMap<Name, MacroDefId>,
/// Some macros are not well-behavior, which leads to infinite loop /// Some macros are not well-behavior, which leads to infinite loop
/// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } } /// e.g. macro_rules! foo { ($ty:ty) => { foo!($ty); } }
@ -106,7 +105,6 @@ pub struct CrateDefMap {
/// However, do we want to put it as a global variable? /// However, do we want to put it as a global variable?
poison_macros: FxHashSet<MacroDefId>, poison_macros: FxHashSet<MacroDefId>,
local_macros: FxHashMap<Name, MacroDefId>,
diagnostics: Vec<DefDiagnostic>, diagnostics: Vec<DefDiagnostic>,
} }
@ -249,9 +247,7 @@ impl CrateDefMap {
prelude: None, prelude: None,
root, root,
modules, modules,
public_macros: FxHashMap::default(),
poison_macros: FxHashSet::default(), poison_macros: FxHashSet::default(),
local_macros: FxHashMap::default(),
diagnostics: Vec::new(), diagnostics: Vec::new(),
} }
}; };
@ -313,7 +309,7 @@ impl CrateDefMap {
(res.resolved_def.left().unwrap_or_else(PerNs::none), res.segment_index) (res.resolved_def.left().unwrap_or_else(PerNs::none), res.segment_index)
} }
fn resolve_path_with_macro( pub(crate) fn resolve_path_with_macro(
&self, &self,
db: &impl DefDatabase, db: &impl DefDatabase,
original_module: CrateModuleId, original_module: CrateModuleId,
@ -323,27 +319,6 @@ impl CrateDefMap {
(res.resolved_def, res.segment_index) (res.resolved_def, res.segment_index)
} }
// FIXME: This seems to do the same work as `resolve_path_with_macro`, but
// using a completely different code path. Seems bad, huh?
pub(crate) fn find_macro(
&self,
db: &impl DefDatabase,
original_module: CrateModuleId,
path: &Path,
) -> Option<MacroDefId> {
let name = path.expand_macro_expr()?;
// search local first
// FIXME: Remove public_macros check when we have a correct local_macors implementation
let local =
self.public_macros.get(&name).or_else(|| self.local_macros.get(&name)).map(|it| *it);
if local.is_some() {
return local;
}
let res = self.resolve_path_fp_with_macro(db, ResolveMode::Other, original_module, path);
res.resolved_def.right().map(|m| m.id)
}
// Returns Yes if we are sure that additions to `ItemMap` wouldn't change // Returns Yes if we are sure that additions to `ItemMap` wouldn't change
// the result. // the result.
fn resolve_path_fp_with_macro( fn resolve_path_fp_with_macro(
@ -511,7 +486,7 @@ impl CrateDefMap {
let from_scope = self[module] let from_scope = self[module]
.scope .scope
.get_item_or_macro(name) .get_item_or_macro(name)
.unwrap_or_else(|| Either::Left(PerNs::none()));; .unwrap_or_else(|| Either::Left(PerNs::none()));
let from_extern_prelude = let from_extern_prelude =
self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it)); self.extern_prelude.get(name).map_or(PerNs::none(), |&it| PerNs::types(it));
let from_prelude = self.resolve_in_prelude(db, name); let from_prelude = self.resolve_in_prelude(db, name);

View file

@ -138,15 +138,35 @@ where
} }
} }
fn define_macro(&mut self, name: Name, macro_id: MacroDefId, export: bool) { fn define_macro(
if export { &mut self,
self.def_map.public_macros.insert(name.clone(), macro_id); module_id: CrateModuleId,
name: Name,
macro_id: MacroDefId,
export: bool,
) {
// macro-by-example in Rust have completely weird name resolution logic,
// unlike anything else in the language. We'd don't fully implement yet,
// just give a somewhat precise approximation.
//
// Specifically, we store a set of visible macros in each module, just
// like how we do with usual items. This is wrong, however, because
// macros can be shadowed and their scopes are mostly unrelated to
// modules. To paper over the second problem, we also maintain
// `global_macro_scope` which works when we construct `CrateDefMap`, but
// is completely ignored in expressions.
//
// What we should do is that, in CrateDefMap, we should maintain a
// separate tower of macro scopes, with ids. Then, for each item in the
// module, we need to store it's macro scope.
let def = Either::Right(MacroDef { id: macro_id });
let def = Either::Right(MacroDef { id: macro_id }); // In Rust, `#[macro_export]` macros are unconditionally visible at the
self.update(self.def_map.root, None, &[(name.clone(), def)]); // crate root, even if the parent modules is **not** visible.
} else { if export {
self.def_map.local_macros.insert(name.clone(), macro_id); self.update(self.def_map.root, None, &[(name.clone(), def.clone())]);
} }
self.update(module_id, None, &[(name.clone(), def)]);
self.global_macro_scope.insert(name, macro_id); self.global_macro_scope.insert(name, macro_id);
} }
@ -589,7 +609,7 @@ where
if is_macro_rules(&mac.path) { if is_macro_rules(&mac.path) {
if let Some(name) = &mac.name { if let Some(name) = &mac.name {
let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id)); let macro_id = MacroDefId(mac.ast_id.with_file_id(self.file_id));
self.def_collector.define_macro(name.clone(), macro_id, mac.export) self.def_collector.define_macro(self.module_id, name.clone(), macro_id, mac.export)
} }
return; return;
} }
@ -694,9 +714,7 @@ mod tests {
prelude: None, prelude: None,
root, root,
modules, modules,
public_macros: FxHashMap::default(),
poison_macros: FxHashSet::default(), poison_macros: FxHashSet::default(),
local_macros: FxHashMap::default(),
diagnostics: Vec::new(), diagnostics: Vec::new(),
} }
}; };

View file

@ -21,6 +21,7 @@ fn macro_rules_are_globally_visible() {
crate crate
Foo: t v Foo: t v
nested: t nested: t
structs: m
crate::nested crate::nested
Bar: t v Bar: t v
@ -46,6 +47,7 @@ fn macro_rules_can_define_modules() {
); );
assert_snapshot_matches!(map, @r###" assert_snapshot_matches!(map, @r###"
crate crate
m: m
n1: t n1: t
crate::n1 crate::n1
@ -127,8 +129,11 @@ fn unexpanded_macro_should_expand_by_fixedpoint_loop() {
"foo": ("/lib.rs", []), "foo": ("/lib.rs", []),
}, },
); );
assert_snapshot_matches!(map, @r###"crate assert_snapshot_matches!(map, @r###"
Foo: t v crate
bar: m Foo: t v
foo: m"###); bar: m
baz: m
foo: m
"###);
} }

View file

@ -2,11 +2,11 @@
use std::sync::Arc; use std::sync::Arc;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use either::Either;
use crate::{ use crate::{
ModuleDef, Trait, ModuleDef, Trait, MacroDef,
code_model::Crate, code_model::Crate,
MacroDefId,
db::HirDatabase, db::HirDatabase,
name::{Name, KnownName}, name::{Name, KnownName},
nameres::{PerNs, CrateDefMap, CrateModuleId}, nameres::{PerNs, CrateDefMap, CrateModuleId},
@ -130,13 +130,16 @@ impl Resolver {
resolution resolution
} }
pub(crate) fn resolve_macro_call( pub(crate) fn resolve_path_as_macro(
&self, &self,
db: &impl HirDatabase, db: &impl HirDatabase,
path: Option<Path>, path: &Path,
) -> Option<MacroDefId> { ) -> Option<MacroDef> {
let m = self.module()?; let (item_map, module) = self.module()?;
m.0.find_macro(db, m.1, &path?) match item_map.resolve_path_with_macro(db, module, path) {
(Either::Right(macro_def), None) => Some(macro_def),
_ => None,
}
} }
/// Returns the resolved path segments /// Returns the resolved path segments
@ -165,7 +168,11 @@ impl Resolver {
/// Returns the fully resolved path if we were able to resolve it. /// Returns the fully resolved path if we were able to resolve it.
/// otherwise returns `PerNs::none` /// otherwise returns `PerNs::none`
pub(crate) fn resolve_path(&self, db: &impl HirDatabase, path: &Path) -> PerNs<Resolution> { pub(crate) fn resolve_path_without_assoc_items(
&self,
db: &impl HirDatabase,
path: &Path,
) -> PerNs<Resolution> {
// into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise // into_fully_resolved() returns the fully resolved path or PerNs::none() otherwise
self.resolve_path_segments(db, path).into_fully_resolved() self.resolve_path_segments(db, path).into_fully_resolved()
} }

View file

@ -267,9 +267,8 @@ impl SourceAnalyzer {
db: &impl HirDatabase, db: &impl HirDatabase,
macro_call: &ast::MacroCall, macro_call: &ast::MacroCall,
) -> Option<MacroDef> { ) -> Option<MacroDef> {
let id = let path = macro_call.path().and_then(Path::from_ast)?;
self.resolver.resolve_macro_call(db, macro_call.path().and_then(Path::from_ast))?; self.resolver.resolve_path_as_macro(db, &path)
Some(MacroDef { id })
} }
pub fn resolve_hir_path( pub fn resolve_hir_path(
@ -277,7 +276,7 @@ impl SourceAnalyzer {
db: &impl HirDatabase, db: &impl HirDatabase,
path: &crate::Path, path: &crate::Path,
) -> PerNs<crate::Resolution> { ) -> PerNs<crate::Resolution> {
self.resolver.resolve_path(db, path) self.resolver.resolve_path_without_assoc_items(db, path)
} }
pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> { pub fn resolve_path(&self, db: &impl HirDatabase, path: &ast::Path) -> Option<PathResolution> {
@ -294,7 +293,7 @@ impl SourceAnalyzer {
} }
} }
let hir_path = crate::Path::from_ast(path)?; let hir_path = crate::Path::from_ast(path)?;
let res = self.resolver.resolve_path(db, &hir_path); let res = self.resolver.resolve_path_without_assoc_items(db, &hir_path);
let res = res.clone().take_types().or_else(|| res.take_values())?; let res = res.clone().take_types().or_else(|| res.take_values())?;
let res = match res { let res = match res {
crate::Resolution::Def(it) => PathResolution::Def(it), crate::Resolution::Def(it) => PathResolution::Def(it),

View file

@ -610,23 +610,26 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
None => return (Ty::Unknown, None), None => return (Ty::Unknown, None),
}; };
let resolver = &self.resolver; let resolver = &self.resolver;
let typable: Option<TypableDef> = match resolver.resolve_path(self.db, &path).take_types() { let typable: Option<TypableDef> =
Some(Resolution::Def(def)) => def.into(), // FIXME: this should resolve assoc items as well, see this example:
Some(Resolution::LocalBinding(..)) => { // https://play.rust-lang.org/?gist=087992e9e22495446c01c0d4e2d69521
// this cannot happen match resolver.resolve_path_without_assoc_items(self.db, &path).take_types() {
log::error!("path resolved to local binding in type ns"); Some(Resolution::Def(def)) => def.into(),
return (Ty::Unknown, None); Some(Resolution::LocalBinding(..)) => {
} // this cannot happen
Some(Resolution::GenericParam(..)) => { log::error!("path resolved to local binding in type ns");
// generic params can't be used in struct literals return (Ty::Unknown, None);
return (Ty::Unknown, None); }
} Some(Resolution::GenericParam(..)) => {
Some(Resolution::SelfType(..)) => { // generic params can't be used in struct literals
// FIXME this is allowed in an impl for a struct, handle this return (Ty::Unknown, None);
return (Ty::Unknown, None); }
} Some(Resolution::SelfType(..)) => {
None => return (Ty::Unknown, None), // FIXME this is allowed in an impl for a struct, handle this
}; return (Ty::Unknown, None);
}
None => return (Ty::Unknown, None),
};
let def = match typable { let def = match typable {
None => return (Ty::Unknown, None), None => return (Ty::Unknown, None),
Some(it) => it, Some(it) => it,

View file

@ -65,7 +65,7 @@ impl Ty {
pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self { pub(crate) fn from_hir_path(db: &impl HirDatabase, resolver: &Resolver, path: &Path) -> Self {
// Resolve the path (in type namespace) // Resolve the path (in type namespace)
let resolution = resolver.resolve_path(db, path).take_types(); let resolution = resolver.resolve_path_without_assoc_items(db, path).take_types();
let def = match resolution { let def = match resolution {
Some(Resolution::Def(def)) => def, Some(Resolution::Def(def)) => def,
@ -216,7 +216,7 @@ impl TraitRef {
path: &Path, path: &Path,
explicit_self_ty: Option<Ty>, explicit_self_ty: Option<Ty>,
) -> Option<Self> { ) -> Option<Self> {
let resolved = match resolver.resolve_path(db, &path).take_types()? { let resolved = match resolver.resolve_path_without_assoc_items(db, &path).take_types()? {
Resolution::Def(ModuleDef::Trait(tr)) => tr, Resolution::Def(ModuleDef::Trait(tr)) => tr,
_ => return None, _ => return None,
}; };

View file

@ -2481,8 +2481,10 @@ fn main() {
} }
"#), "#),
@r###" @r###"
[156; 182) '{ ...,2); }': ()
[166; 167) 'x': Foo"### [156; 182) '{ ...,2); }': ()
[166; 167) 'x': Foo
"###
); );
} }