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.rs328
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),
+ }
+}