Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir/src/source_analyzer.rs')
| -rw-r--r-- | crates/hir/src/source_analyzer.rs | 328 |
1 files changed, 254 insertions, 74 deletions
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 4329a888b3..b699ccde41 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -9,8 +9,8 @@ use std::iter::{self, once}; use crate::{ db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr, - BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static, - Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, + BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro, + ModuleDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant, }; use either::Either; use hir_def::{ @@ -18,15 +18,15 @@ use hir_def::{ scope::{ExprScopes, ScopeId}, Body, BodySourceMap, HygieneId, }, - hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId}, + hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId}, lang_item::LangItem, lower::LowerCtx, nameres::MacroSubNs, path::{ModPath, Path, PathKind}, resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs}, type_ref::{Mutability, TypesMap, TypesSourceMap}, - AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId, - LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, + AsMacroCall, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId, + ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId, }; use hir_expand::{ mod_path::path, @@ -38,9 +38,10 @@ use hir_ty::{ record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions, InsideUnsafeBlock, }, + from_assoc_type_id, lang_items::lang_items_for_bin_op, - method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind, - TyLoweringContext, + method_resolution, Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty, + TyExt, TyKind, TyLoweringContext, }; use intern::sym; use itertools::Itertools; @@ -120,6 +121,13 @@ impl SourceAnalyzer { self.def.as_ref().map(|(_, body, _)| &**body) } + fn trait_environment(&self, db: &dyn HirDatabase) -> Arc<TraitEnvironment> { + self.def.as_ref().map(|(def, ..)| *def).map_or_else( + || TraitEnvironment::empty(self.resolver.krate()), + |def| db.trait_environment_for_body(def), + ) + } + fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> { let src = match expr { ast::Expr::MacroExpr(expr) => { @@ -294,18 +302,23 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, call: &ast::MethodCallExpr, - ) -> Option<Either<Function, Field>> { + ) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> { let expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.method_resolution(expr_id) { - Some((f_in_trait, substs)) => Some(Either::Left( - self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(), - )), - None => inference_result - .field_resolution(expr_id) - .and_then(Either::left) - .map(Into::into) - .map(Either::Right), + Some((f_in_trait, substs)) => { + let (fn_, subst) = + self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs); + Some(( + Either::Left(fn_.into()), + Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))), + )) + } + None => { + inference_result.field_resolution(expr_id).and_then(Either::left).map(|field| { + (Either::Right(field.into()), self.field_subst(expr_id, inference_result, db)) + }) + } } } @@ -330,22 +343,53 @@ impl SourceAnalyzer { }) } + fn field_subst( + &self, + field_expr: ExprId, + infer: &InferenceResult, + db: &dyn HirDatabase, + ) -> Option<GenericSubstitution> { + let body = self.body()?; + if let Expr::Field { expr: object_expr, name: _ } = body[field_expr] { + let (adt, subst) = type_of_expr_including_adjust(infer, object_expr)?.as_adt()?; + return Some(GenericSubstitution::new( + adt.into(), + subst.clone(), + self.trait_environment(db), + )); + } + None + } + pub(crate) fn resolve_field_fallback( &self, db: &dyn HirDatabase, field: &ast::FieldExpr, - ) -> Option<Either<Either<Field, TupleField>, Function>> { + ) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> { let &(def, ..) = self.def.as_ref()?; let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?; let inference_result = self.infer.as_ref()?; match inference_result.field_resolution(expr_id) { - Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField { - owner: def, - tuple: f.tuple, - index: f.index, - }))), + Some(field) => match field { + Either::Left(field) => Some(( + Either::Left(Either::Left(field.into())), + self.field_subst(expr_id, inference_result, db), + )), + Either::Right(field) => Some(( + Either::Left(Either::Right(TupleField { + owner: def, + tuple: field.tuple, + index: field.index, + })), + None, + )), + }, None => inference_result.method_resolution(expr_id).map(|(f, substs)| { - Either::Right(self.resolve_impl_method_or_trait_def(db, f, substs).into()) + let (f, subst) = self.resolve_impl_method_or_trait_def_with_subst(db, f, substs); + ( + Either::Right(f.into()), + Some(GenericSubstitution::new(f.into(), subst, self.trait_environment(db))), + ) }), } } @@ -557,7 +601,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, field: &ast::RecordExprField, - ) -> Option<(Field, Option<Local>, Type)> { + ) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> { let record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let expr = ast::Expr::from(record_expr); let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?; @@ -583,30 +627,39 @@ impl SourceAnalyzer { _ => None, } }; - let (_, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_expr_or_pat(expr_id)?.as_adt()?; let variant = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? }; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), local, Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + local, + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_record_pat_field( &self, db: &dyn HirDatabase, field: &ast::RecordPatField, - ) -> Option<(Field, Type)> { + ) -> Option<(Field, Type, GenericSubstitution)> { let field_name = field.field_name()?.as_name(); let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?; let pat_id = self.pat_id(&record_pat.into())?; let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?; let variant_data = variant.variant_data(db.upcast()); let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? }; - let (_, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; + let (adt, subst) = self.infer.as_ref()?.type_of_pat.get(pat_id)?.as_adt()?; let field_ty = db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst); - Some((field.into(), Type::new_with_resolver(db, &self.resolver, field_ty))) + Some(( + field.into(), + Type::new_with_resolver(db, &self.resolver, field_ty), + GenericSubstitution::new(adt.into(), subst.clone(), self.trait_environment(db)), + )) } pub(crate) fn resolve_macro_call( @@ -654,7 +707,7 @@ impl SourceAnalyzer { &self, db: &dyn HirDatabase, path: &ast::Path, - ) -> Option<PathResolution> { + ) -> Option<(PathResolution, Option<GenericSubstitution>)> { let parent = path.syntax().parent(); let parent = || parent.clone(); @@ -664,60 +717,106 @@ impl SourceAnalyzer { if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) { let expr_id = self.expr_id(db, &path_expr.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::FunctionId(f_in_trait) => { match infer.type_of_expr_or_pat(expr_id) { - None => assoc, + None => { + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) + } Some(func_ty) => { if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) { - self.resolve_impl_method_or_trait_def( - db, - f_in_trait, - subs.clone(), - ) - .into() + let (fn_, subst) = self + .resolve_impl_method_or_trait_def_with_subst( + db, + f_in_trait, + subs.clone(), + ); + let subst = GenericSubstitution::new( + fn_.into(), + subst, + self.trait_environment(db), + ); + (fn_.into(), subst) } else { - assoc + let subst = GenericSubstitution::new( + f_in_trait.into(), + subs, + self.trait_environment(db), + ); + (assoc, subst) } } } } AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + AssocItemId::TypeAliasId(type_alias) => ( + assoc, + GenericSubstitution::new( + type_alias.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } prefer_value_ns = true; } else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) { let pat_id = self.pat_id(&path_pat.into())?; if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) { - let assoc = match assoc { + let (assoc, subst) = match assoc { AssocItemId::ConstId(const_id) => { - self.resolve_impl_const_or_trait_def(db, const_id, subs).into() + let (konst, subst) = + self.resolve_impl_const_or_trait_def_with_subst(db, const_id, subs); + let subst = GenericSubstitution::new( + konst.into(), + subst, + self.trait_environment(db), + ); + (konst.into(), subst) } - assoc => assoc, + assoc => ( + assoc, + GenericSubstitution::new( + assoc.into(), + subs, + self.trait_environment(db), + ), + ), }; - return Some(PathResolution::Def(AssocItem::from(assoc).into())); + return Some((PathResolution::Def(AssocItem::from(assoc).into()), Some(subst))); } if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_pat(pat_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) { let expr_id = self.expr_id(db, &rec_lit.into())?; if let Some(VariantId::EnumVariantId(variant)) = infer.variant_resolution_for_expr_or_pat(expr_id) { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some((PathResolution::Def(ModuleDef::Variant(variant.into())), None)); } } else { let record_pat = parent().and_then(ast::RecordPat::cast).map(ast::Pat::from); @@ -727,7 +826,10 @@ impl SourceAnalyzer { let pat_id = self.pat_id(&pat)?; let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id); if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat { - return Some(PathResolution::Def(ModuleDef::Variant(variant.into()))); + return Some(( + PathResolution::Def(ModuleDef::Variant(variant.into())), + None, + )); } } } @@ -747,7 +849,8 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if let Some(use_tree) = parent().and_then(ast::UseTree::cast) { if use_tree.coloncolon_token().is_some() { - return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map); + return resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)); } } @@ -765,13 +868,18 @@ impl SourceAnalyzer { // trying to resolve foo::bar. if path.parent_path().is_some() { return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) { - None if meta_path.is_some() => { - path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { + None if meta_path.is_some() => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) .map(PathResolution::ToolModule) }) - } - res => res, + .map(|it| (it, None)), + // FIXME: We do not show substitutions for parts of path, because this is really complex + // due to the interactions with associated items of `impl`s and associated items of associated + // types. + res => res.map(|it| (it, None)), }; } else if let Some(meta_path) = meta_path { // Case where we are resolving the final path segment of a path in an attribute @@ -781,7 +889,7 @@ impl SourceAnalyzer { let builtin = BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text()); if builtin.is_some() { - return builtin.map(PathResolution::BuiltinAttr); + return builtin.map(|it| (PathResolution::BuiltinAttr(it), None)); } if let Some(attr) = meta_path.parent_attr() { @@ -814,10 +922,13 @@ impl SourceAnalyzer { { if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref) { - return Some(PathResolution::DeriveHelper(DeriveHelper { - derive: *macro_id, - idx: idx as u32, - })); + return Some(( + PathResolution::DeriveHelper(DeriveHelper { + derive: *macro_id, + idx: idx as u32, + }), + None, + )); } } } @@ -825,26 +936,79 @@ impl SourceAnalyzer { } } return match resolve_hir_path_as_attr_macro(db, &self.resolver, &hir_path) { - Some(m) => Some(PathResolution::Def(ModuleDef::Macro(m))), + Some(m) => Some((PathResolution::Def(ModuleDef::Macro(m)), None)), // this labels any path that starts with a tool module as the tool itself, this is technically wrong // but there is no benefit in differentiating these two cases for the time being - None => path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| { - ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) - .map(PathResolution::ToolModule) - }), + None => path + .first_segment() + .and_then(|it| it.name_ref()) + .and_then(|name_ref| { + ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text()) + .map(PathResolution::ToolModule) + }) + .map(|it| (it, None)), }; } - if parent().map_or(false, |it| ast::Visibility::can_cast(it.kind())) { + if parent().is_some_and(|it| ast::Visibility::can_cast(it.kind())) { + // No substitution because only modules can be inside visibilities, and those have no generics. resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) + .map(|it| (it, None)) } else { - resolve_hir_path_( + // Probably a type, no need to show substitutions for those. + let res = resolve_hir_path_( db, &self.resolver, &hir_path, prefer_value_ns, name_hygiene(db, InFile::new(self.file_id, path.syntax())), &types_map, - ) + )?; + let subst = (|| { + let parent = parent()?; + let ty = if let Some(expr) = ast::Expr::cast(parent.clone()) { + let expr_id = self.expr_id(db, &expr)?; + self.infer.as_ref()?.type_of_expr_or_pat(expr_id)? + } else if let Some(pat) = ast::Pat::cast(parent) { + let pat_id = self.pat_id(&pat)?; + &self.infer.as_ref()?[pat_id] + } else { + return None; + }; + let env = self.trait_environment(db); + let (subst, expected_resolution) = match ty.kind(Interner) { + TyKind::Adt(adt_id, subst) => ( + GenericSubstitution::new(adt_id.0.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::Adt(adt_id.0.into())), + ), + TyKind::AssociatedType(assoc_id, subst) => { + let assoc_id = from_assoc_type_id(*assoc_id); + ( + GenericSubstitution::new(assoc_id.into(), subst.clone(), env), + PathResolution::Def(ModuleDef::TypeAlias(assoc_id.into())), + ) + } + TyKind::FnDef(fn_id, subst) => { + let fn_id = hir_ty::db::InternedCallableDefId::from(*fn_id); + let fn_id = db.lookup_intern_callable_def(fn_id); + let generic_def_id = match fn_id { + CallableDefId::StructId(id) => id.into(), + CallableDefId::FunctionId(id) => id.into(), + CallableDefId::EnumVariantId(_) => return None, + }; + ( + GenericSubstitution::new(generic_def_id, subst.clone(), env), + PathResolution::Def(ModuleDefId::from(fn_id).into()), + ) + } + _ => return None, + }; + if res != expected_resolution { + // The user will not understand where we're coming from. This can happen (I think) with type aliases. + return None; + } + Some(subst) + })(); + Some((res, subst)) } } @@ -1041,26 +1205,35 @@ impl SourceAnalyzer { func: FunctionId, substs: Substitution, ) -> FunctionId { + self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0 + } + + fn resolve_impl_method_or_trait_def_with_subst( + &self, + db: &dyn HirDatabase, + func: FunctionId, + substs: Substitution, + ) -> (FunctionId, Substitution) { let owner = match self.resolver.body_owner() { Some(it) => it, - None => return func, + None => return (func, substs), }; let env = db.trait_environment_for_body(owner); - db.lookup_impl_method(env, func, substs).0 + db.lookup_impl_method(env, func, substs) } - fn resolve_impl_const_or_trait_def( + fn resolve_impl_const_or_trait_def_with_subst( &self, db: &dyn HirDatabase, const_id: ConstId, subs: Substitution, - ) -> ConstId { + ) -> (ConstId, Substitution) { let owner = match self.resolver.body_owner() { Some(it) => it, - None => return const_id, + None => return (const_id, subs), }; let env = db.trait_environment_for_body(owner); - method_resolution::lookup_impl_const(db, env, const_id, subs).0 + method_resolution::lookup_impl_const(db, env, const_id, subs) } fn lang_trait_fn( @@ -1413,3 +1586,10 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H let ctx = db.lookup_intern_syntax_context(ctx); HygieneId::new(ctx.opaque_and_semitransparent) } + +fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> { + match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) { + Some(adjustment) => Some(&adjustment.target), + None => infer.type_of_expr.get(id), + } +} |