Unnamed repository; edit this file 'description' to name the repository.
fix: Fix glob imports overriding later specific imports
| -rw-r--r-- | crates/hir-def/src/item_scope.rs | 32 | ||||
| -rw-r--r-- | crates/hir-def/src/per_ns.rs | 10 | ||||
| -rw-r--r-- | crates/ide-diagnostics/src/handlers/type_mismatch.rs | 25 |
3 files changed, 55 insertions, 12 deletions
diff --git a/crates/hir-def/src/item_scope.rs b/crates/hir-def/src/item_scope.rs index 1443d3ea4b..14f10651cb 100644 --- a/crates/hir-def/src/item_scope.rs +++ b/crates/hir-def/src/item_scope.rs @@ -621,7 +621,11 @@ impl ItemScope { // for that. } _ => { - if glob_imports.types.remove(&lookup) { + // A non-glob import either shadows a glob import of the same + // name, or re-resolves a stale binding it recorded earlier. + if glob_imports.types.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { let prev = std::mem::replace(&mut fld.import, import); if let Some(import) = import { self.use_imports_types.insert( @@ -659,19 +663,22 @@ impl ItemScope { changed = true; } Entry::Occupied(mut entry) - if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.values.remove(&lookup) => + if !matches!(import, Some(ImportOrExternCrate::Glob(..))) => { - cov_mark::hit!(import_shadowed); - let import = import.and_then(ImportOrExternCrate::import_or_glob); - let prev = std::mem::replace(&mut fld.import, import); - if let Some(import) = import { - self.use_imports_values - .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + if glob_imports.values.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import) + { + cov_mark::hit!(import_shadowed); + + let prev = std::mem::replace(&mut fld.import, import); + if let Some(import) = import { + self.use_imports_values + .insert(import, prev.map_or(ImportOrDef::Def(fld.def), Into::into)); + } + entry.insert(fld); + changed = true; } - entry.insert(fld); - changed = true; } _ => {} } @@ -699,7 +706,8 @@ impl ItemScope { } Entry::Occupied(mut entry) if !matches!(import, Some(ImportOrExternCrate::Glob(..))) - && glob_imports.macros.remove(&lookup) => + && (glob_imports.macros.remove(&lookup) + || entry.get().is_reresolved_by(&fld.def, import)) => { cov_mark::hit!(import_shadowed); let prev = std::mem::replace(&mut fld.import, import); diff --git a/crates/hir-def/src/per_ns.rs b/crates/hir-def/src/per_ns.rs index 8721cd65db..62947f1511 100644 --- a/crates/hir-def/src/per_ns.rs +++ b/crates/hir-def/src/per_ns.rs @@ -35,6 +35,16 @@ pub struct Item<Def, Import = ImportId> { pub import: Option<Import>, } +impl<Def: PartialEq, Import: Copy + PartialEq> Item<Def, Import> { + /// Whether `import` is the same import that produced `self`, now resolving to a different + /// `def`. This happens when an import is first recorded as an indeterminate resolution + /// (e.g. only one namespace was available at the time) and later re-resolves to another + /// def, such as an explicit import that shadows a glob only after the glob has been seen. + pub(crate) fn is_reresolved_by(&self, def: &Def, import: Option<Import>) -> bool { + import.is_some() && self.import == import && self.def != *def + } +} + pub type TypesItem = Item<ModuleDefId, ImportOrExternCrate>; pub type ValuesItem = Item<ModuleDefId, ImportOrGlob>; // May be Externcrate for `[macro_use]`'d macros diff --git a/crates/ide-diagnostics/src/handlers/type_mismatch.rs b/crates/ide-diagnostics/src/handlers/type_mismatch.rs index 295f37ab1d..b16be503ef 100644 --- a/crates/ide-diagnostics/src/handlers/type_mismatch.rs +++ b/crates/ide-diagnostics/src/handlers/type_mismatch.rs @@ -1813,4 +1813,29 @@ fn main() { "#, ); } + + #[test] + fn regression_21668() { + check_diagnostics( + r#" +mod std { + pub enum Ordering { Less } +} +pub use std::*; + +pub mod evil { + pub struct Ordering(pub i32); +} + +pub mod oblivious { + use crate::Ordering; + + pub fn what() -> Ordering { + Ordering(2) + } +} +pub use evil::Ordering; +"#, + ); + } } |