mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 13:51:31 +00:00
Merge pull request #18707 from ChayimFriedman2/subst
feat: Show substitution where hovering over generic things
This commit is contained in:
commit
e30ce42671
29 changed files with 1019 additions and 190 deletions
|
@ -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(¯o_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,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
) =>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue