mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-30 19:49:36 +00:00 
			
		
		
		
	Show substitution where hovering over generic things
There are few things to note in the implementation: First, this is a best-effort implementation. Mainly, type aliases may not be shown (due to their eager nature it's harder) and partial pathes (aka. hovering over `Struct` in `Struct::method`) are not supported at all. Second, we only need to show substitutions in expression and pattern position, because in type position all generic arguments always have to be written explicitly.
This commit is contained in:
		
							parent
							
								
									27e824fad4
								
							
						
					
					
						commit
						b5486ffc42
					
				
					 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, | ||||
|         } | ||||
|     } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Chayim Refael Friedman
						Chayim Refael Friedman