Skip to content

Commit

Permalink
Show substitution where hovering over generic things
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
ChayimFriedman2 committed Dec 20, 2024
1 parent 27e824f commit b5486ff
Show file tree
Hide file tree
Showing 29 changed files with 1,015 additions and 186 deletions.
55 changes: 55 additions & 0 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3574,6 +3574,61 @@ impl GenericDef {
}
}

// We cannot call this `Substitution` unfortunately...
#[derive(Debug)]
pub struct GenericSubstitution {
def: GenericDefId,
subst: Substitution,
env: Arc<TraitEnvironment>,
}

impl GenericSubstitution {
fn new(def: GenericDefId, subst: Substitution, env: Arc<TraitEnvironment>) -> Self {
Self { def, subst, env }
}

pub fn types(&self, db: &dyn HirDatabase) -> Vec<(Symbol, Type)> {
let container = match self.def {
GenericDefId::ConstId(id) => Some(id.lookup(db.upcast()).container),
GenericDefId::FunctionId(id) => Some(id.lookup(db.upcast()).container),
GenericDefId::TypeAliasId(id) => Some(id.lookup(db.upcast()).container),
_ => None,
};
let container_type_params = container
.and_then(|container| match container {
ItemContainerId::ImplId(container) => Some(container.into()),
ItemContainerId::TraitId(container) => Some(container.into()),
_ => None,
})
.map(|container| {
db.generic_params(container)
.iter_type_or_consts()
.filter_map(|param| match param.1 {
TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()),
TypeOrConstParamData::ConstParamData(_) => None,
})
.collect::<Vec<_>>()
});
let generics = db.generic_params(self.def);
let type_params = generics.iter_type_or_consts().filter_map(|param| match param.1 {
TypeOrConstParamData::TypeParamData(param) => Some(param.name.clone()),
TypeOrConstParamData::ConstParamData(_) => None,
});
// The `Substitution` is first self then container, we want the reverse order.
let self_params = self.subst.type_parameters(Interner).zip(type_params);
let container_params = self.subst.as_slice(Interner)[generics.len()..]
.iter()
.filter_map(|param| param.ty(Interner).cloned())
.zip(container_type_params.into_iter().flatten());
container_params
.chain(self_params)
.filter_map(|(ty, name)| {
Some((name?.symbol().clone(), Type { ty, env: self.env.clone() }))
})
.collect()
}
}

/// A single local definition.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Local {
Expand Down
34 changes: 28 additions & 6 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ use crate::{
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField,
Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
};

const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
Expand Down Expand Up @@ -1415,7 +1415,7 @@ impl<'db> SemanticsImpl<'db> {
pub fn resolve_method_call_fallback(
&self,
call: &ast::MethodCallExpr,
) -> Option<Either<Function, Field>> {
) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
}

Expand Down Expand Up @@ -1458,18 +1458,33 @@ impl<'db> SemanticsImpl<'db> {
pub fn resolve_field_fallback(
&self,
field: &ast::FieldExpr,
) -> Option<Either<Either<Field, TupleField>, Function>> {
) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
}

pub fn resolve_record_field(
&self,
field: &ast::RecordExprField,
) -> Option<(Field, Option<Local>, Type)> {
self.resolve_record_field_with_substitution(field)
.map(|(field, local, ty, _)| (field, local, ty))
}

pub fn resolve_record_field_with_substitution(
&self,
field: &ast::RecordExprField,
) -> Option<(Field, Option<Local>, Type, GenericSubstitution)> {
self.analyze(field.syntax())?.resolve_record_field(self.db, field)
}

pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
self.resolve_record_pat_field_with_subst(field).map(|(field, ty, _)| (field, ty))
}

pub fn resolve_record_pat_field_with_subst(
&self,
field: &ast::RecordPatField,
) -> Option<(Field, Type, GenericSubstitution)> {
self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
}

Expand Down Expand Up @@ -1525,6 +1540,13 @@ impl<'db> SemanticsImpl<'db> {
}

pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
self.resolve_path_with_subst(path).map(|(it, _)| it)
}

pub fn resolve_path_with_subst(
&self,
path: &ast::Path,
) -> Option<(PathResolution, Option<GenericSubstitution>)> {
self.analyze(path.syntax())?.resolve_path(self.db, path)
}

Expand Down
Loading

0 comments on commit b5486ff

Please sign in to comment.