mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
Merge #2466
2466: Handle partial resolve cases r=matklad a=edwin0cheng Another try to fix #2443 : We resolve all imports every time in `DefCollector::collect` loop even it is resolved previously. This is because other unresolved imports and macros will bring in another `PerNs`, so we can only assume that it has been partially resolved. Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
b236f6aa49
4 changed files with 194 additions and 44 deletions
|
@ -58,6 +58,8 @@ pub(super) fn collect_defs(db: &impl DefDatabase, mut def_map: CrateDefMap) -> C
|
||||||
def_map,
|
def_map,
|
||||||
glob_imports: FxHashMap::default(),
|
glob_imports: FxHashMap::default(),
|
||||||
unresolved_imports: Vec::new(),
|
unresolved_imports: Vec::new(),
|
||||||
|
resolved_imports: Vec::new(),
|
||||||
|
|
||||||
unexpanded_macros: Vec::new(),
|
unexpanded_macros: Vec::new(),
|
||||||
unexpanded_attribute_macros: Vec::new(),
|
unexpanded_attribute_macros: Vec::new(),
|
||||||
mod_dirs: FxHashMap::default(),
|
mod_dirs: FxHashMap::default(),
|
||||||
|
@ -97,12 +99,41 @@ impl MacroStackMonitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
|
enum PartialResolvedImport {
|
||||||
|
/// None of any namespaces is resolved
|
||||||
|
Unresolved,
|
||||||
|
/// One of namespaces is resolved
|
||||||
|
Indeterminate(PerNs),
|
||||||
|
/// All namespaces are resolved, OR it is came from other crate
|
||||||
|
Resolved(PerNs),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialResolvedImport {
|
||||||
|
fn namespaces(&self) -> PerNs {
|
||||||
|
match self {
|
||||||
|
PartialResolvedImport::Unresolved => PerNs::none(),
|
||||||
|
PartialResolvedImport::Indeterminate(ns) => *ns,
|
||||||
|
PartialResolvedImport::Resolved(ns) => *ns,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
struct ImportDirective {
|
||||||
|
module_id: LocalModuleId,
|
||||||
|
import_id: LocalImportId,
|
||||||
|
import: raw::ImportData,
|
||||||
|
status: PartialResolvedImport,
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the tree of module recursively
|
/// Walks the tree of module recursively
|
||||||
struct DefCollector<'a, DB> {
|
struct DefCollector<'a, DB> {
|
||||||
db: &'a DB,
|
db: &'a DB,
|
||||||
def_map: CrateDefMap,
|
def_map: CrateDefMap,
|
||||||
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>,
|
glob_imports: FxHashMap<LocalModuleId, Vec<(LocalModuleId, LocalImportId)>>,
|
||||||
unresolved_imports: Vec<(LocalModuleId, LocalImportId, raw::ImportData)>,
|
unresolved_imports: Vec<ImportDirective>,
|
||||||
|
resolved_imports: Vec<ImportDirective>,
|
||||||
unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>,
|
unexpanded_macros: Vec<(LocalModuleId, AstId<ast::MacroCall>, Path)>,
|
||||||
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>,
|
unexpanded_attribute_macros: Vec<(LocalModuleId, AstId<ast::ModuleItem>, Path)>,
|
||||||
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
mod_dirs: FxHashMap<LocalModuleId, ModDir>,
|
||||||
|
@ -148,9 +179,11 @@ where
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
loop {
|
loop {
|
||||||
self.db.check_canceled();
|
self.db.check_canceled();
|
||||||
match (self.resolve_imports(), self.resolve_macros()) {
|
self.resolve_imports();
|
||||||
(ReachedFixedPoint::Yes, ReachedFixedPoint::Yes) => break,
|
|
||||||
_ => i += 1,
|
match self.resolve_macros() {
|
||||||
|
ReachedFixedPoint::Yes => break,
|
||||||
|
ReachedFixedPoint::No => i += 1,
|
||||||
}
|
}
|
||||||
if i == 1000 {
|
if i == 1000 {
|
||||||
log::error!("name resolution is stuck");
|
log::error!("name resolution is stuck");
|
||||||
|
@ -158,10 +191,26 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve all indeterminate resolved imports again
|
||||||
|
// As some of the macros will expand newly import shadowing partial resolved imports
|
||||||
|
// FIXME: We maybe could skip this, if we handle the Indetermine imports in `resolve_imports`
|
||||||
|
// correctly
|
||||||
|
let partial_resolved = self.resolved_imports.iter().filter_map(|directive| {
|
||||||
|
if let PartialResolvedImport::Indeterminate(_) = directive.status {
|
||||||
|
let mut directive = directive.clone();
|
||||||
|
directive.status = PartialResolvedImport::Unresolved;
|
||||||
|
Some(directive)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
});
|
||||||
|
self.unresolved_imports.extend(partial_resolved);
|
||||||
|
self.resolve_imports();
|
||||||
|
|
||||||
let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
let unresolved_imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||||
// show unresolved imports in completion, etc
|
// show unresolved imports in completion, etc
|
||||||
for (module_id, import, import_data) in unresolved_imports {
|
for directive in unresolved_imports {
|
||||||
self.record_resolved_import(module_id, PerNs::none(), import, &import_data)
|
self.record_resolved_import(&directive)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,31 +311,43 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_imports(&mut self) -> ReachedFixedPoint {
|
/// Import resolution
|
||||||
let mut imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
///
|
||||||
let mut resolved = Vec::new();
|
/// This is a fix point algorithm. We resolve imports until no forward
|
||||||
imports.retain(|(module_id, import, import_data)| {
|
/// progress in resolving imports is made
|
||||||
let (def, fp) = self.resolve_import(*module_id, import_data);
|
fn resolve_imports(&mut self) {
|
||||||
if fp == ReachedFixedPoint::Yes {
|
let mut n_previous_unresolved = self.unresolved_imports.len() + 1;
|
||||||
resolved.push((*module_id, def, *import, import_data.clone()))
|
|
||||||
|
while self.unresolved_imports.len() < n_previous_unresolved {
|
||||||
|
n_previous_unresolved = self.unresolved_imports.len();
|
||||||
|
let imports = std::mem::replace(&mut self.unresolved_imports, Vec::new());
|
||||||
|
for mut directive in imports {
|
||||||
|
directive.status = self.resolve_import(directive.module_id, &directive.import);
|
||||||
|
|
||||||
|
match directive.status {
|
||||||
|
PartialResolvedImport::Indeterminate(_) => {
|
||||||
|
self.record_resolved_import(&directive);
|
||||||
|
// FIXME: For avoid performance regression,
|
||||||
|
// we consider an imported resolved if it is indeterminate (i.e not all namespace resolved)
|
||||||
|
self.resolved_imports.push(directive)
|
||||||
|
}
|
||||||
|
PartialResolvedImport::Resolved(_) => {
|
||||||
|
self.record_resolved_import(&directive);
|
||||||
|
self.resolved_imports.push(directive)
|
||||||
|
}
|
||||||
|
PartialResolvedImport::Unresolved => {
|
||||||
|
self.unresolved_imports.push(directive);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fp == ReachedFixedPoint::No
|
|
||||||
});
|
|
||||||
self.unresolved_imports = imports;
|
|
||||||
// Resolves imports, filling-in module scopes
|
|
||||||
let result =
|
|
||||||
if resolved.is_empty() { ReachedFixedPoint::Yes } else { ReachedFixedPoint::No };
|
|
||||||
for (module_id, def, import, import_data) in resolved {
|
|
||||||
self.record_resolved_import(module_id, def, import, &import_data)
|
|
||||||
}
|
}
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_import(
|
fn resolve_import(
|
||||||
&self,
|
&self,
|
||||||
module_id: LocalModuleId,
|
module_id: LocalModuleId,
|
||||||
import: &raw::ImportData,
|
import: &raw::ImportData,
|
||||||
) -> (PerNs, ReachedFixedPoint) {
|
) -> PartialResolvedImport {
|
||||||
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
log::debug!("resolving import: {:?} ({:?})", import, self.def_map.edition);
|
||||||
if import.is_extern_crate {
|
if import.is_extern_crate {
|
||||||
let res = self.def_map.resolve_name_in_extern_prelude(
|
let res = self.def_map.resolve_name_in_extern_prelude(
|
||||||
|
@ -295,7 +356,7 @@ where
|
||||||
.as_ident()
|
.as_ident()
|
||||||
.expect("extern crate should have been desugared to one-element path"),
|
.expect("extern crate should have been desugared to one-element path"),
|
||||||
);
|
);
|
||||||
(res, ReachedFixedPoint::Yes)
|
PartialResolvedImport::Resolved(res)
|
||||||
} else {
|
} else {
|
||||||
let res = self.def_map.resolve_path_fp_with_macro(
|
let res = self.def_map.resolve_path_fp_with_macro(
|
||||||
self.db,
|
self.db,
|
||||||
|
@ -305,17 +366,35 @@ where
|
||||||
BuiltinShadowMode::Module,
|
BuiltinShadowMode::Module,
|
||||||
);
|
);
|
||||||
|
|
||||||
(res.resolved_def, res.reached_fixedpoint)
|
let def = res.resolved_def;
|
||||||
|
if res.reached_fixedpoint == ReachedFixedPoint::No {
|
||||||
|
return PartialResolvedImport::Unresolved;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(krate) = res.krate {
|
||||||
|
if krate != self.def_map.krate {
|
||||||
|
return PartialResolvedImport::Resolved(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check whether all namespace is resolved
|
||||||
|
if def.take_types().is_some()
|
||||||
|
&& def.take_values().is_some()
|
||||||
|
&& def.take_macros().is_some()
|
||||||
|
{
|
||||||
|
PartialResolvedImport::Resolved(def)
|
||||||
|
} else {
|
||||||
|
PartialResolvedImport::Indeterminate(def)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn record_resolved_import(
|
fn record_resolved_import(&mut self, directive: &ImportDirective) {
|
||||||
&mut self,
|
let module_id = directive.module_id;
|
||||||
module_id: LocalModuleId,
|
let import_id = directive.import_id;
|
||||||
def: PerNs,
|
let import = &directive.import;
|
||||||
import_id: LocalImportId,
|
let def = directive.status.namespaces();
|
||||||
import: &raw::ImportData,
|
|
||||||
) {
|
|
||||||
if import.is_glob {
|
if import.is_glob {
|
||||||
log::debug!("glob import: {:?}", import);
|
log::debug!("glob import: {:?}", import);
|
||||||
match def.take_types() {
|
match def.take_types() {
|
||||||
|
@ -352,10 +431,10 @@ where
|
||||||
|
|
||||||
self.update(module_id, Some(import_id), &items);
|
self.update(module_id, Some(import_id), &items);
|
||||||
// record the glob import in case we add further items
|
// record the glob import in case we add further items
|
||||||
self.glob_imports
|
let glob = self.glob_imports.entry(m.local_id).or_default();
|
||||||
.entry(m.local_id)
|
if !glob.iter().any(|it| *it == (module_id, import_id)) {
|
||||||
.or_default()
|
glob.push((module_id, import_id));
|
||||||
.push((module_id, import_id));
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
Some(ModuleDefId::AdtId(AdtId::EnumId(e))) => {
|
||||||
|
@ -615,10 +694,14 @@ where
|
||||||
raw::RawItemKind::Module(m) => {
|
raw::RawItemKind::Module(m) => {
|
||||||
self.collect_module(&self.raw_items[m], &item.attrs)
|
self.collect_module(&self.raw_items[m], &item.attrs)
|
||||||
}
|
}
|
||||||
raw::RawItemKind::Import(import_id) => self
|
raw::RawItemKind::Import(import_id) => {
|
||||||
.def_collector
|
self.def_collector.unresolved_imports.push(ImportDirective {
|
||||||
.unresolved_imports
|
module_id: self.module_id,
|
||||||
.push((self.module_id, import_id, self.raw_items[import_id].clone())),
|
import_id,
|
||||||
|
import: self.raw_items[import_id].clone(),
|
||||||
|
status: PartialResolvedImport::Unresolved,
|
||||||
|
})
|
||||||
|
}
|
||||||
raw::RawItemKind::Def(def) => {
|
raw::RawItemKind::Def(def) => {
|
||||||
self.define_def(&self.raw_items[def], &item.attrs)
|
self.define_def(&self.raw_items[def], &item.attrs)
|
||||||
}
|
}
|
||||||
|
@ -886,6 +969,7 @@ mod tests {
|
||||||
def_map,
|
def_map,
|
||||||
glob_imports: FxHashMap::default(),
|
glob_imports: FxHashMap::default(),
|
||||||
unresolved_imports: Vec::new(),
|
unresolved_imports: Vec::new(),
|
||||||
|
resolved_imports: Vec::new(),
|
||||||
unexpanded_macros: Vec::new(),
|
unexpanded_macros: Vec::new(),
|
||||||
unexpanded_attribute_macros: Vec::new(),
|
unexpanded_attribute_macros: Vec::new(),
|
||||||
mod_dirs: FxHashMap::default(),
|
mod_dirs: FxHashMap::default(),
|
||||||
|
|
|
@ -19,7 +19,7 @@ use crate::{
|
||||||
nameres::{BuiltinShadowMode, CrateDefMap},
|
nameres::{BuiltinShadowMode, CrateDefMap},
|
||||||
path::{Path, PathKind},
|
path::{Path, PathKind},
|
||||||
per_ns::PerNs,
|
per_ns::PerNs,
|
||||||
AdtId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
AdtId, CrateId, EnumVariantId, LocalModuleId, ModuleDefId, ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
@ -39,19 +39,21 @@ pub(super) struct ResolvePathResult {
|
||||||
pub(super) resolved_def: PerNs,
|
pub(super) resolved_def: PerNs,
|
||||||
pub(super) segment_index: Option<usize>,
|
pub(super) segment_index: Option<usize>,
|
||||||
pub(super) reached_fixedpoint: ReachedFixedPoint,
|
pub(super) reached_fixedpoint: ReachedFixedPoint,
|
||||||
|
pub(super) krate: Option<CrateId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvePathResult {
|
impl ResolvePathResult {
|
||||||
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
fn empty(reached_fixedpoint: ReachedFixedPoint) -> ResolvePathResult {
|
||||||
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None)
|
ResolvePathResult::with(PerNs::none(), reached_fixedpoint, None, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn with(
|
fn with(
|
||||||
resolved_def: PerNs,
|
resolved_def: PerNs,
|
||||||
reached_fixedpoint: ReachedFixedPoint,
|
reached_fixedpoint: ReachedFixedPoint,
|
||||||
segment_index: Option<usize>,
|
segment_index: Option<usize>,
|
||||||
|
krate: Option<CrateId>,
|
||||||
) -> ResolvePathResult {
|
) -> ResolvePathResult {
|
||||||
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index }
|
ResolvePathResult { resolved_def, reached_fixedpoint, segment_index, krate }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,6 +177,7 @@ impl CrateDefMap {
|
||||||
def,
|
def,
|
||||||
ReachedFixedPoint::Yes,
|
ReachedFixedPoint::Yes,
|
||||||
s.map(|s| s + i),
|
s.map(|s| s + i),
|
||||||
|
Some(module.krate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,6 +204,7 @@ impl CrateDefMap {
|
||||||
PerNs::types(e.into()),
|
PerNs::types(e.into()),
|
||||||
ReachedFixedPoint::Yes,
|
ReachedFixedPoint::Yes,
|
||||||
Some(i),
|
Some(i),
|
||||||
|
Some(self.krate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,12 +222,13 @@ impl CrateDefMap {
|
||||||
PerNs::types(s),
|
PerNs::types(s),
|
||||||
ReachedFixedPoint::Yes,
|
ReachedFixedPoint::Yes,
|
||||||
Some(i),
|
Some(i),
|
||||||
|
Some(self.krate),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None)
|
ResolvePathResult::with(curr_per_ns, ReachedFixedPoint::Yes, None, Some(self.krate))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_name_in_module(
|
fn resolve_name_in_module(
|
||||||
|
|
|
@ -558,3 +558,35 @@ fn cfg_test() {
|
||||||
⋮Foo: t v
|
⋮Foo: t v
|
||||||
"###);
|
"###);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_multiple_namespace() {
|
||||||
|
let map = def_map(
|
||||||
|
r#"
|
||||||
|
//- /main.rs
|
||||||
|
mod a {
|
||||||
|
pub type T = ();
|
||||||
|
pub use crate::b::*;
|
||||||
|
}
|
||||||
|
|
||||||
|
use crate::a::T;
|
||||||
|
|
||||||
|
mod b {
|
||||||
|
pub const T: () = ();
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_snapshot!(map, @r###"
|
||||||
|
⋮crate
|
||||||
|
⋮T: t v
|
||||||
|
⋮a: t
|
||||||
|
⋮b: t
|
||||||
|
⋮
|
||||||
|
⋮crate::b
|
||||||
|
⋮T: v
|
||||||
|
⋮
|
||||||
|
⋮crate::a
|
||||||
|
⋮T: t v
|
||||||
|
"###);
|
||||||
|
}
|
||||||
|
|
|
@ -210,6 +210,35 @@ pub fn baz() -> usize { 31usize }
|
||||||
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
assert_eq!("(i32, usize)", type_at_pos(&db, pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_type_value_non_legacy_macro_use_as() {
|
||||||
|
assert_snapshot!(
|
||||||
|
infer(r#"
|
||||||
|
mod m {
|
||||||
|
macro_rules! _foo {
|
||||||
|
($x:ident) => { type $x = u64; }
|
||||||
|
}
|
||||||
|
pub(crate) use _foo as foo;
|
||||||
|
}
|
||||||
|
|
||||||
|
m::foo!(foo);
|
||||||
|
use foo as bar;
|
||||||
|
fn f() -> bar { 0 }
|
||||||
|
fn main() {
|
||||||
|
let _a = f();
|
||||||
|
}
|
||||||
|
"#),
|
||||||
|
@r###"
|
||||||
|
[159; 164) '{ 0 }': u64
|
||||||
|
[161; 162) '0': u64
|
||||||
|
[175; 199) '{ ...f(); }': ()
|
||||||
|
[187; 189) '_a': u64
|
||||||
|
[193; 194) 'f': fn f() -> u64
|
||||||
|
[193; 196) 'f()': u64
|
||||||
|
"###
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn infer_builtin_macros_line() {
|
fn infer_builtin_macros_line() {
|
||||||
assert_snapshot!(
|
assert_snapshot!(
|
||||||
|
|
Loading…
Reference in a new issue