Merge pull request #18707 from ChayimFriedman2/subst

feat: Show substitution where hovering over generic things
This commit is contained in:
Lukas Wirth 2024-12-24 14:16:16 +00:00 committed by GitHub
commit e30ce42671
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 1019 additions and 190 deletions

View file

@ -13,9 +13,10 @@ use either::Either;
use hir::{
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
Function, GenericParam, GenericSubstitution, HasVisibility, HirDisplay, Impl, InlineAsmOperand,
Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static,
StaticLifetime, Struct, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant,
VariantDef, Visibility,
};
use span::Edition;
use stdx::{format_to, impl_from};
@ -359,24 +360,32 @@ impl IdentClass {
.or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
}
pub fn definitions(self) -> ArrayVec<Definition, 2> {
pub fn definitions(self) -> ArrayVec<(Definition, Option<GenericSubstitution>), 2> {
let mut res = ArrayVec::new();
match self {
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
res.push(it)
res.push((it, None))
}
IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
res.push(Definition::Local(local_def));
res.push(Definition::Field(field_ref));
IdentClass::NameClass(NameClass::PatFieldShorthand {
local_def,
field_ref,
adt_subst,
}) => {
res.push((Definition::Local(local_def), None));
res.push((Definition::Field(field_ref), Some(adt_subst)));
}
IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
res.push(Definition::Local(local_ref));
res.push(Definition::Field(field_ref));
IdentClass::NameRefClass(NameRefClass::Definition(it, subst)) => res.push((it, subst)),
IdentClass::NameRefClass(NameRefClass::FieldShorthand {
local_ref,
field_ref,
adt_subst,
}) => {
res.push((Definition::Local(local_ref), None));
res.push((Definition::Field(field_ref), Some(adt_subst)));
}
IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
res.push(Definition::ExternCrateDecl(decl));
res.push(Definition::Module(krate.root_module()));
res.push((Definition::ExternCrateDecl(decl), None));
res.push((Definition::Module(krate.root_module()), None));
}
IdentClass::Operator(
OperatorClass::Await(func)
@ -384,9 +393,9 @@ impl IdentClass {
| OperatorClass::Bin(func)
| OperatorClass::Index(func)
| OperatorClass::Try(func),
) => res.push(Definition::Function(func)),
) => res.push((Definition::Function(func), None)),
IdentClass::Operator(OperatorClass::Range(struct0)) => {
res.push(Definition::Adt(Adt::Struct(struct0)))
res.push((Definition::Adt(Adt::Struct(struct0)), None))
}
}
res
@ -398,12 +407,20 @@ impl IdentClass {
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
res.push(it)
}
IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
IdentClass::NameClass(NameClass::PatFieldShorthand {
local_def,
field_ref,
adt_subst: _,
}) => {
res.push(Definition::Local(local_def));
res.push(Definition::Field(field_ref));
}
IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it),
IdentClass::NameRefClass(NameRefClass::FieldShorthand {
local_ref,
field_ref,
adt_subst: _,
}) => {
res.push(Definition::Local(local_ref));
res.push(Definition::Field(field_ref));
}
@ -437,6 +454,7 @@ pub enum NameClass {
PatFieldShorthand {
local_def: Local,
field_ref: Field,
adt_subst: GenericSubstitution,
},
}
@ -446,7 +464,7 @@ impl NameClass {
let res = match self {
NameClass::Definition(it) => it,
NameClass::ConstReference(_) => return None,
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
Definition::Local(local_def)
}
};
@ -517,10 +535,13 @@ impl NameClass {
let pat_parent = ident_pat.syntax().parent();
if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
if record_pat_field.name_ref().is_none() {
if let Some((field, _)) = sema.resolve_record_pat_field(&record_pat_field) {
if let Some((field, _, adt_subst)) =
sema.resolve_record_pat_field_with_subst(&record_pat_field)
{
return Some(NameClass::PatFieldShorthand {
local_def: local,
field_ref: field,
adt_subst,
});
}
}
@ -629,10 +650,11 @@ impl OperatorClass {
/// reference to point to two different defs.
#[derive(Debug)]
pub enum NameRefClass {
Definition(Definition),
Definition(Definition, Option<GenericSubstitution>),
FieldShorthand {
local_ref: Local,
field_ref: Field,
adt_subst: GenericSubstitution,
},
/// The specific situation where we have an extern crate decl without a rename
/// Here we have both a declaration and a reference.
@ -657,12 +679,16 @@ impl NameRefClass {
let parent = name_ref.syntax().parent()?;
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
if let Some((field, local, _)) = sema.resolve_record_field(&record_field) {
if let Some((field, local, _, adt_subst)) =
sema.resolve_record_field_with_substitution(&record_field)
{
let res = match local {
None => NameRefClass::Definition(Definition::Field(field)),
Some(local) => {
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
}
None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)),
Some(local) => NameRefClass::FieldShorthand {
field_ref: field,
local_ref: local,
adt_subst,
},
};
return Some(res);
}
@ -674,44 +700,43 @@ impl NameRefClass {
// Only use this to resolve to macro calls for last segments as qualifiers resolve
// to modules below.
if let Some(macro_def) = sema.resolve_macro_call(&macro_call) {
return Some(NameRefClass::Definition(Definition::Macro(macro_def)));
return Some(NameRefClass::Definition(Definition::Macro(macro_def), None));
}
}
}
return sema.resolve_path(&path).map(Into::into).map(NameRefClass::Definition);
return sema
.resolve_path_with_subst(&path)
.map(|(res, subst)| NameRefClass::Definition(res.into(), subst));
}
match_ast! {
match parent {
ast::MethodCallExpr(method_call) => {
sema.resolve_method_call_fallback(&method_call)
.map(|it| {
it.map_left(Definition::Function)
.map_right(Definition::Field)
.either(NameRefClass::Definition, NameRefClass::Definition)
.map(|(def, subst)| {
match def {
Either::Left(def) => NameRefClass::Definition(def.into(), subst),
Either::Right(def) => NameRefClass::Definition(def.into(), subst),
}
})
},
ast::FieldExpr(field_expr) => {
sema.resolve_field_fallback(&field_expr)
.map(|it| {
NameRefClass::Definition(match it {
Either::Left(Either::Left(field)) => Definition::Field(field),
Either::Left(Either::Right(field)) => Definition::TupleField(field),
Either::Right(fun) => Definition::Function(fun),
.map(|(def, subst)| {
match def {
Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst),
Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst),
Either::Right(def) => NameRefClass::Definition(def.into(), subst),
}
})
})
},
ast::RecordPatField(record_pat_field) => {
sema.resolve_record_pat_field(&record_pat_field)
.map(|(field, ..)|field)
.map(Definition::Field)
.map(NameRefClass::Definition)
sema.resolve_record_pat_field_with_subst(&record_pat_field)
.map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
},
ast::RecordExprField(record_expr_field) => {
sema.resolve_record_field(&record_expr_field)
.map(|(field, ..)|field)
.map(Definition::Field)
.map(NameRefClass::Definition)
sema.resolve_record_field_with_substitution(&record_expr_field)
.map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
},
ast::AssocTypeArg(_) => {
// `Trait<Assoc = Ty>`
@ -728,28 +753,30 @@ impl NameRefClass {
})
.find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
{
return Some(NameRefClass::Definition(Definition::TypeAlias(ty)));
// No substitution, this can only occur in type position.
return Some(NameRefClass::Definition(Definition::TypeAlias(ty), None));
}
}
None
},
ast::UseBoundGenericArgs(_) => {
// No substitution, this can only occur in type position.
sema.resolve_use_type_arg(name_ref)
.map(GenericParam::TypeParam)
.map(Definition::GenericParam)
.map(NameRefClass::Definition)
.map(|it| NameRefClass::Definition(it, None))
},
ast::ExternCrate(extern_crate_ast) => {
let extern_crate = sema.to_def(&extern_crate_ast)?;
let krate = extern_crate.resolved_crate(sema.db)?;
Some(if extern_crate_ast.rename().is_some() {
NameRefClass::Definition(Definition::Module(krate.root_module()))
NameRefClass::Definition(Definition::Module(krate.root_module()), None)
} else {
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
})
},
ast::AsmRegSpec(_) => {
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(())))
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None))
},
_ => None
}
@ -762,13 +789,17 @@ impl NameRefClass {
) -> Option<NameRefClass> {
let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
if lifetime.text() == "'static" {
return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime)));
return Some(NameRefClass::Definition(
Definition::BuiltinLifetime(StaticLifetime),
None,
));
}
let parent = lifetime.syntax().parent()?;
match parent.kind() {
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
}
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema
.resolve_label(lifetime)
.map(Definition::Label)
.map(|it| NameRefClass::Definition(it, None)),
SyntaxKind::LIFETIME_ARG
| SyntaxKind::USE_BOUND_GENERIC_ARGS
| SyntaxKind::SELF_PARAM
@ -778,7 +809,7 @@ impl NameRefClass {
.resolve_lifetime_param(lifetime)
.map(GenericParam::LifetimeParam)
.map(Definition::GenericParam)
.map(NameRefClass::Definition),
.map(|it| NameRefClass::Definition(it, None)),
_ => None,
}
}

View file

@ -1081,7 +1081,7 @@ impl<'a> FindUsages<'a> {
};
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(Definition::SelfType(impl_)))
Some(NameRefClass::Definition(Definition::SelfType(impl_), _))
if ty_eq(impl_.self_ty(self.sema.db)) =>
{
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
@ -1102,7 +1102,7 @@ impl<'a> FindUsages<'a> {
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(def @ Definition::Module(_))) if def == self.def => {
Some(NameRefClass::Definition(def @ Definition::Module(_), _)) if def == self.def => {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let category = if is_name_ref_in_import(name_ref) {
ReferenceCategory::IMPORT
@ -1147,7 +1147,7 @@ impl<'a> FindUsages<'a> {
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
match NameRefClass::classify_lifetime(self.sema, lifetime) {
Some(NameRefClass::Definition(def)) if def == self.def => {
Some(NameRefClass::Definition(def, _)) if def == self.def => {
let FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
let reference = FileReference {
range,
@ -1166,7 +1166,7 @@ impl<'a> FindUsages<'a> {
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
match NameRefClass::classify(self.sema, name_ref) {
Some(NameRefClass::Definition(def))
Some(NameRefClass::Definition(def, _))
if self.def == def
// is our def a trait assoc item? then we want to find all assoc items from trait impls of our trait
|| matches!(self.assoc_item_container, Some(hir::AssocItemContainer::Trait(_)))
@ -1182,7 +1182,7 @@ impl<'a> FindUsages<'a> {
}
// FIXME: special case type aliases, we can't filter between impl and trait defs here as we lack the substitutions
// so we always resolve all assoc type aliases to both their trait def and impl defs
Some(NameRefClass::Definition(def))
Some(NameRefClass::Definition(def, _))
if self.assoc_item_container.is_some()
&& matches!(self.def, Definition::TypeAlias(_))
&& convert_to_def_in_trait(self.sema.db, def)
@ -1196,7 +1196,7 @@ impl<'a> FindUsages<'a> {
};
sink(file_id, reference)
}
Some(NameRefClass::Definition(def)) if self.include_self_kw_refs.is_some() => {
Some(NameRefClass::Definition(def, _)) if self.include_self_kw_refs.is_some() => {
if self.include_self_kw_refs == def_to_ty(self.sema, &def) {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let reference = FileReference {
@ -1209,7 +1209,11 @@ impl<'a> FindUsages<'a> {
false
}
}
Some(NameRefClass::FieldShorthand { local_ref: local, field_ref: field }) => {
Some(NameRefClass::FieldShorthand {
local_ref: local,
field_ref: field,
adt_subst: _,
}) => {
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
let field = Definition::Field(field);
@ -1240,7 +1244,7 @@ impl<'a> FindUsages<'a> {
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
) -> bool {
match NameClass::classify(self.sema, name) {
Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ })
if matches!(
self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
) =>