Merge pull request #19742 from Veykril/push-ykmuwtkzruqm

fix: Fix incorrect handling of unresolved non-module imports in name resolution
This commit is contained in:
Lukas Wirth 2025-05-05 06:41:34 +00:00 committed by GitHub
commit b1bd478029
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 303 additions and 22 deletions

View File

@ -701,6 +701,16 @@ pub enum AssocItemId {
// casting them, and somehow making the constructors private, which would be annoying. // casting them, and somehow making the constructors private, which would be annoying.
impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId); impl_from!(FunctionId, ConstId, TypeAliasId for AssocItemId);
impl From<AssocItemId> for ModuleDefId {
fn from(item: AssocItemId) -> Self {
match item {
AssocItemId::FunctionId(f) => f.into(),
AssocItemId::ConstId(c) => c.into(),
AssocItemId::TypeAliasId(t) => t.into(),
}
}
}
#[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)] #[derive(Debug, PartialOrd, Ord, Clone, Copy, PartialEq, Eq, Hash, salsa_macros::Supertype)]
pub enum GenericDefId { pub enum GenericDefId {
AdtId(AdtId), AdtId(AdtId),

View File

@ -66,6 +66,15 @@ impl TraitItems {
}) })
} }
pub fn assoc_item_by_name(&self, name: &Name) -> Option<AssocItemId> {
self.items.iter().find_map(|&(ref item_name, item)| match item {
AssocItemId::FunctionId(_) if item_name == name => Some(item),
AssocItemId::TypeAliasId(_) if item_name == name => Some(item),
AssocItemId::ConstId(_) if item_name == name => Some(item),
_ => None,
})
}
pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ { pub fn attribute_calls(&self) -> impl Iterator<Item = (AstId<ast::Item>, MacroCallId)> + '_ {
self.macro_calls.iter().flat_map(|it| it.iter()).copied() self.macro_calls.iter().flat_map(|it| it.iter()).copied()
} }

View File

@ -26,7 +26,7 @@ use syntax::ast;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
AdtId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc, AdtId, AssocItemId, AstId, AstIdWithPath, ConstLoc, CrateRootModuleId, EnumLoc, ExternBlockLoc,
ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId, ExternCrateId, ExternCrateLoc, FunctionId, FunctionLoc, ImplLoc, Intern, ItemContainerId,
LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId, LocalModuleId, Lookup, Macro2Id, Macro2Loc, MacroExpander, MacroId, MacroRulesId,
MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc, MacroRulesLoc, MacroRulesLocFlags, ModuleDefId, ModuleId, ProcMacroId, ProcMacroLoc, StaticLoc,
@ -45,7 +45,7 @@ use crate::{
attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id}, attr_resolution::{attr_macro_as_call_id, derive_macro_as_call_id},
diagnostics::DefDiagnostic, diagnostics::DefDiagnostic,
mod_resolution::ModDir, mod_resolution::ModDir,
path_resolution::ReachedFixedPoint, path_resolution::{ReachedFixedPoint, ResolvePathResult},
proc_macro::{ProcMacroDef, ProcMacroKind, parse_macro_name_and_helper_attrs}, proc_macro::{ProcMacroDef, ProcMacroKind, parse_macro_name_and_helper_attrs},
sub_namespace_match, sub_namespace_match,
}, },
@ -811,32 +811,35 @@ impl DefCollector<'_> {
let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST)) let _p = tracing::info_span!("resolve_import", import_path = %import.path.display(self.db, Edition::LATEST))
.entered(); .entered();
tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition); tracing::debug!("resolving import: {:?} ({:?})", import, self.def_map.data.edition);
let res = self.def_map.resolve_path_fp_with_macro( let ResolvePathResult { resolved_def, segment_index, reached_fixedpoint, prefix_info } =
self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map), self.def_map.resolve_path_fp_with_macro(
self.db, self.crate_local_def_map.as_deref().unwrap_or(&self.local_def_map),
ResolveMode::Import, self.db,
module_id, ResolveMode::Import,
&import.path, module_id,
BuiltinShadowMode::Module, &import.path,
None, // An import may resolve to any kind of macro. BuiltinShadowMode::Module,
); None, // An import may resolve to any kind of macro.
);
let def = res.resolved_def; if reached_fixedpoint == ReachedFixedPoint::No
if res.reached_fixedpoint == ReachedFixedPoint::No || def.is_none() { || resolved_def.is_none()
|| segment_index.is_some()
{
return PartialResolvedImport::Unresolved; return PartialResolvedImport::Unresolved;
} }
if res.prefix_info.differing_crate { if prefix_info.differing_crate {
return PartialResolvedImport::Resolved( return PartialResolvedImport::Resolved(
def.filter_visibility(|v| matches!(v, Visibility::Public)), resolved_def.filter_visibility(|v| matches!(v, Visibility::Public)),
); );
} }
// Check whether all namespaces are resolved. // Check whether all namespaces are resolved.
if def.is_full() { if resolved_def.is_full() {
PartialResolvedImport::Resolved(def) PartialResolvedImport::Resolved(resolved_def)
} else { } else {
PartialResolvedImport::Indeterminate(def) PartialResolvedImport::Indeterminate(resolved_def)
} }
} }
@ -986,6 +989,43 @@ impl DefCollector<'_> {
Some(ImportOrExternCrate::Glob(glob)), Some(ImportOrExternCrate::Glob(glob)),
); );
} }
Some(ModuleDefId::TraitId(it)) => {
// FIXME: Implement this correctly
// We can't actually call `trait_items`, the reason being that if macro calls
// occur, they will call back into the def map which we might be computing right
// now resulting in a cycle.
// To properly implement this, trait item collection needs to be done in def map
// collection...
let resolutions = if true {
vec![]
} else {
self.db
.trait_items(it)
.items
.iter()
.map(|&(ref name, variant)| {
let res = match variant {
AssocItemId::FunctionId(it) => {
PerNs::values(it.into(), vis, None)
}
AssocItemId::ConstId(it) => {
PerNs::values(it.into(), vis, None)
}
AssocItemId::TypeAliasId(it) => {
PerNs::types(it.into(), vis, None)
}
};
(Some(name.clone()), res)
})
.collect::<Vec<_>>()
};
self.update(
module_id,
&resolutions,
vis,
Some(ImportOrExternCrate::Glob(glob)),
);
}
Some(d) => { Some(d) => {
tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d); tracing::debug!("glob import {:?} from non-module/enum {:?}", import, d);
} }

View File

@ -17,6 +17,7 @@ use hir_expand::{
name::Name, name::Name,
}; };
use span::Edition; use span::Edition;
use stdx::TupleExt;
use triomphe::Arc; use triomphe::Arc;
use crate::{ use crate::{
@ -44,6 +45,7 @@ pub(super) enum ReachedFixedPoint {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(super) struct ResolvePathResult { pub(super) struct ResolvePathResult {
pub(super) resolved_def: PerNs, pub(super) resolved_def: PerNs,
/// The index of the last resolved segment, or `None` if the full path has been resolved.
pub(super) segment_index: Option<usize>, pub(super) segment_index: Option<usize>,
pub(super) reached_fixedpoint: ReachedFixedPoint, pub(super) reached_fixedpoint: ReachedFixedPoint,
pub(super) prefix_info: ResolvePathResultPrefixInfo, pub(super) prefix_info: ResolvePathResultPrefixInfo,
@ -364,7 +366,15 @@ impl DefMap {
}, },
}; };
self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) self.resolve_remaining_segments(
db,
mode,
segments,
curr_per_ns,
path,
shadow,
original_module,
)
} }
/// Resolves a path only in the preludes, without accounting for item scopes. /// Resolves a path only in the preludes, without accounting for item scopes.
@ -413,7 +423,15 @@ impl DefMap {
} }
}; };
self.resolve_remaining_segments(segments, curr_per_ns, path, db, shadow, original_module) self.resolve_remaining_segments(
db,
mode,
segments,
curr_per_ns,
path,
shadow,
original_module,
)
} }
/// 2018-style absolute path -- only extern prelude /// 2018-style absolute path -- only extern prelude
@ -441,10 +459,11 @@ impl DefMap {
fn resolve_remaining_segments<'a>( fn resolve_remaining_segments<'a>(
&self, &self,
db: &dyn DefDatabase,
mode: ResolveMode,
mut segments: impl Iterator<Item = (usize, &'a Name)>, mut segments: impl Iterator<Item = (usize, &'a Name)>,
mut curr_per_ns: PerNs, mut curr_per_ns: PerNs,
path: &ModPath, path: &ModPath,
db: &dyn DefDatabase,
shadow: BuiltinShadowMode, shadow: BuiltinShadowMode,
original_module: LocalModuleId, original_module: LocalModuleId,
) -> ResolvePathResult { ) -> ResolvePathResult {
@ -478,7 +497,7 @@ impl DefMap {
let resolution = defp_map.resolve_path_fp_with_macro( let resolution = defp_map.resolve_path_fp_with_macro(
LocalDefMap::EMPTY, LocalDefMap::EMPTY,
db, db,
ResolveMode::Other, mode,
module.local_id, module.local_id,
&path, &path,
shadow, shadow,
@ -553,6 +572,44 @@ impl DefMap {
), ),
}; };
} }
def @ ModuleDefId::TraitId(t) if mode == ResolveMode::Import => {
// FIXME: Implement this correctly
// We can't actually call `trait_items`, the reason being that if macro calls
// occur, they will call back into the def map which we might be computing right
// now resulting in a cycle.
// To properly implement this, trait item collection needs to be done in def map
// collection...
let item =
if true { None } else { db.trait_items(t).assoc_item_by_name(segment) };
return match item {
Some(item) => ResolvePathResult::new(
match item {
crate::AssocItemId::FunctionId(function_id) => PerNs::values(
function_id.into(),
curr.vis,
curr.import.and_then(|it| it.import_or_glob()),
),
crate::AssocItemId::ConstId(const_id) => PerNs::values(
const_id.into(),
curr.vis,
curr.import.and_then(|it| it.import_or_glob()),
),
crate::AssocItemId::TypeAliasId(type_alias_id) => {
PerNs::types(type_alias_id.into(), curr.vis, curr.import)
}
},
ReachedFixedPoint::Yes,
segments.next().map(TupleExt::head),
ResolvePathResultPrefixInfo::default(),
),
None => ResolvePathResult::new(
PerNs::types(def, curr.vis, curr.import),
ReachedFixedPoint::Yes,
Some(i),
ResolvePathResultPrefixInfo::default(),
),
};
}
s => { s => {
// could be an inherent method call in UFCS form // could be an inherent method call in UFCS form
// (`Struct::method`), or some other kind of associated item // (`Struct::method`), or some other kind of associated item

View File

@ -894,3 +894,149 @@ struct AlsoShouldNotAppear;
"#]], "#]],
) )
} }
#[test]
fn invalid_imports() {
check(
r#"
//- /main.rs
mod module;
use self::module::S::new;
use self::module::unresolved;
use self::module::C::const_based;
use self::module::Enum::Variant::NoAssoc;
//- /module.rs
pub struct S;
impl S {
pub fn new() {}
}
pub const C: () = ();
pub enum Enum {
Variant,
}
"#,
expect![[r#"
crate
NoAssoc: _
const_based: _
module: t
new: _
unresolved: _
crate::module
C: v
Enum: t
S: t v
"#]],
);
}
#[test]
fn trait_item_imports_same_crate() {
check(
r#"
//- /main.rs
mod module;
use self::module::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method};
//- /module.rs
macro_rules! m {
($name:ident) => { const $name: () = (); };
}
pub trait Trait {
type AssocType;
const ASSOC_CONST: ();
fn method(&self);
m!(MACRO_CONST);
}
"#,
expect![[r#"
crate
ASSOC_CONST: _
AssocType: _
MACRO_CONST: _
method: _
module: t
crate::module
Trait: t
"#]],
);
check(
r#"
//- /main.rs
mod module;
use self::module::Trait::*;
//- /module.rs
macro_rules! m {
($name:ident) => { const $name: () = (); };
}
pub trait Trait {
type AssocType;
const ASSOC_CONST: ();
fn method(&self);
m!(MACRO_CONST);
}
"#,
expect![[r#"
crate
module: t
crate::module
Trait: t
"#]],
);
}
#[test]
fn trait_item_imports_differing_crate() {
check(
r#"
//- /main.rs deps:lib crate:main
use lib::Trait::{AssocType, ASSOC_CONST, MACRO_CONST, method};
//- /lib.rs crate:lib
macro_rules! m {
($name:ident) => { const $name: () = (); };
}
pub trait Trait {
type AssocType;
const ASSOC_CONST: ();
fn method(&self);
m!(MACRO_CONST);
}
"#,
expect![[r#"
crate
ASSOC_CONST: _
AssocType: _
MACRO_CONST: _
method: _
"#]],
);
check(
r#"
//- /main.rs deps:lib crate:main
use lib::Trait::*;
//- /lib.rs crate:lib
macro_rules! m {
($name:ident) => { const $name: () = (); };
}
pub trait Trait {
type AssocType;
const ASSOC_CONST: ();
fn method(&self);
m!(MACRO_CONST);
}
"#,
expect![[r#"
crate
"#]],
);
}

View File

@ -4884,3 +4884,22 @@ async fn baz<T: AsyncFnOnce(u32) -> i32>(c: T) {
"#]], "#]],
); );
} }
#[test]
fn import_trait_items() {
check_infer(
r#"
//- minicore: default
use core::default::Default::default;
fn main() {
let a: i32 = default();
}
"#,
expect![[r#"
47..78 '{ ...t(); }': ()
57..58 'a': i32
66..73 'default': {unknown}
66..75 'default()': i32
"#]],
);
}