mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 05:23:24 +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
|
@ -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.
|
/// A single local definition.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct Local {
|
pub struct Local {
|
||||||
|
|
|
@ -49,10 +49,10 @@ use crate::{
|
||||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||||
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
|
source_analyzer::{name_hygiene, resolve_hir_path, SourceAnalyzer},
|
||||||
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
Access, Adjust, Adjustment, Adt, AutoBorrow, BindingMode, BuiltinAttr, Callable, Const,
|
||||||
ConstParam, Crate, DeriveHelper, Enum, Field, Function, HasSource, HirFileId, Impl, InFile,
|
ConstParam, Crate, DeriveHelper, Enum, Field, Function, GenericSubstitution, HasSource,
|
||||||
InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name,
|
HirFileId, Impl, InFile, InlineAsmOperand, ItemInNs, Label, LifetimeParam, Local, Macro,
|
||||||
OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField,
|
Module, ModuleDef, Name, OverloadedDeref, Path, ScopeDef, Static, Struct, ToolModule, Trait,
|
||||||
Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
|
TraitAlias, TupleField, Type, TypeAlias, TypeParam, Union, Variant, VariantDef,
|
||||||
};
|
};
|
||||||
|
|
||||||
const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
|
const CONTINUE_NO_BREAKS: ControlFlow<Infallible, ()> = ControlFlow::Continue(());
|
||||||
|
@ -1415,7 +1415,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
pub fn resolve_method_call_fallback(
|
pub fn resolve_method_call_fallback(
|
||||||
&self,
|
&self,
|
||||||
call: &ast::MethodCallExpr,
|
call: &ast::MethodCallExpr,
|
||||||
) -> Option<Either<Function, Field>> {
|
) -> Option<(Either<Function, Field>, Option<GenericSubstitution>)> {
|
||||||
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1458,7 +1458,7 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
pub fn resolve_field_fallback(
|
pub fn resolve_field_fallback(
|
||||||
&self,
|
&self,
|
||||||
field: &ast::FieldExpr,
|
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)
|
self.analyze(field.syntax())?.resolve_field_fallback(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1466,10 +1466,25 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
&self,
|
&self,
|
||||||
field: &ast::RecordExprField,
|
field: &ast::RecordExprField,
|
||||||
) -> Option<(Field, Option<Local>, Type)> {
|
) -> 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)
|
self.analyze(field.syntax())?.resolve_record_field(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_record_pat_field(&self, field: &ast::RecordPatField) -> Option<(Field, Type)> {
|
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)
|
self.analyze(field.syntax())?.resolve_record_pat_field(self.db, field)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1525,6 +1540,13 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resolve_path(&self, path: &ast::Path) -> Option<PathResolution> {
|
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)
|
self.analyze(path.syntax())?.resolve_path(self.db, path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,8 @@ use std::iter::{self, once};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
|
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
|
||||||
BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
|
BuiltinType, Callable, Const, DeriveHelper, Field, Function, GenericSubstitution, Local, Macro,
|
||||||
Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
|
ModuleDef, Static, Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
|
@ -18,15 +18,15 @@ use hir_def::{
|
||||||
scope::{ExprScopes, ScopeId},
|
scope::{ExprScopes, ScopeId},
|
||||||
Body, BodySourceMap, HygieneId,
|
Body, BodySourceMap, HygieneId,
|
||||||
},
|
},
|
||||||
hir::{BindingId, ExprId, ExprOrPatId, Pat, PatId},
|
hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
lower::LowerCtx,
|
lower::LowerCtx,
|
||||||
nameres::MacroSubNs,
|
nameres::MacroSubNs,
|
||||||
path::{ModPath, Path, PathKind},
|
path::{ModPath, Path, PathKind},
|
||||||
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
||||||
type_ref::{Mutability, TypesMap, TypesSourceMap},
|
type_ref::{Mutability, TypesMap, TypesSourceMap},
|
||||||
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
|
AsMacroCall, AssocItemId, CallableDefId, ConstId, DefWithBodyId, FieldId, FunctionId,
|
||||||
LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
|
ItemContainerId, LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
mod_path::path,
|
mod_path::path,
|
||||||
|
@ -38,9 +38,10 @@ use hir_ty::{
|
||||||
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
record_literal_missing_fields, record_pattern_missing_fields, unsafe_expressions,
|
||||||
InsideUnsafeBlock,
|
InsideUnsafeBlock,
|
||||||
},
|
},
|
||||||
|
from_assoc_type_id,
|
||||||
lang_items::lang_items_for_bin_op,
|
lang_items::lang_items_for_bin_op,
|
||||||
method_resolution, Adjustment, InferenceResult, Interner, Substitution, Ty, TyExt, TyKind,
|
method_resolution, Adjustment, InferenceResult, Interner, Substitution, TraitEnvironment, Ty,
|
||||||
TyLoweringContext,
|
TyExt, TyKind, TyLoweringContext,
|
||||||
};
|
};
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -120,6 +121,13 @@ impl SourceAnalyzer {
|
||||||
self.def.as_ref().map(|(_, body, _)| &**body)
|
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> {
|
fn expr_id(&self, db: &dyn HirDatabase, expr: &ast::Expr) -> Option<ExprOrPatId> {
|
||||||
let src = match expr {
|
let src = match expr {
|
||||||
ast::Expr::MacroExpr(expr) => {
|
ast::Expr::MacroExpr(expr) => {
|
||||||
|
@ -294,18 +302,23 @@ impl SourceAnalyzer {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
call: &ast::MethodCallExpr,
|
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 expr_id = self.expr_id(db, &call.clone().into())?.as_expr()?;
|
||||||
let inference_result = self.infer.as_ref()?;
|
let inference_result = self.infer.as_ref()?;
|
||||||
match inference_result.method_resolution(expr_id) {
|
match inference_result.method_resolution(expr_id) {
|
||||||
Some((f_in_trait, substs)) => Some(Either::Left(
|
Some((f_in_trait, substs)) => {
|
||||||
self.resolve_impl_method_or_trait_def(db, f_in_trait, substs).into(),
|
let (fn_, subst) =
|
||||||
)),
|
self.resolve_impl_method_or_trait_def_with_subst(db, f_in_trait, substs);
|
||||||
None => inference_result
|
Some((
|
||||||
.field_resolution(expr_id)
|
Either::Left(fn_.into()),
|
||||||
.and_then(Either::left)
|
Some(GenericSubstitution::new(fn_.into(), subst, self.trait_environment(db))),
|
||||||
.map(Into::into)
|
))
|
||||||
.map(Either::Right),
|
}
|
||||||
|
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(
|
pub(crate) fn resolve_field_fallback(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
field: &ast::FieldExpr,
|
field: &ast::FieldExpr,
|
||||||
) -> Option<Either<Either<Field, TupleField>, Function>> {
|
) -> Option<(Either<Either<Field, TupleField>, Function>, Option<GenericSubstitution>)> {
|
||||||
let &(def, ..) = self.def.as_ref()?;
|
let &(def, ..) = self.def.as_ref()?;
|
||||||
let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
|
let expr_id = self.expr_id(db, &field.clone().into())?.as_expr()?;
|
||||||
let inference_result = self.infer.as_ref()?;
|
let inference_result = self.infer.as_ref()?;
|
||||||
match inference_result.field_resolution(expr_id) {
|
match inference_result.field_resolution(expr_id) {
|
||||||
Some(field) => Some(Either::Left(field.map_either(Into::into, |f| TupleField {
|
Some(field) => match field {
|
||||||
owner: def,
|
Either::Left(field) => Some((
|
||||||
tuple: f.tuple,
|
Either::Left(Either::Left(field.into())),
|
||||||
index: f.index,
|
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)| {
|
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,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
field: &ast::RecordExprField,
|
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 record_expr = ast::RecordExpr::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
|
||||||
let expr = ast::Expr::from(record_expr);
|
let expr = ast::Expr::from(record_expr);
|
||||||
let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
|
let expr_id = self.body_source_map()?.node_expr(InFile::new(self.file_id, &expr))?;
|
||||||
|
@ -583,30 +627,39 @@ impl SourceAnalyzer {
|
||||||
_ => None,
|
_ => 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 = self.infer.as_ref()?.variant_resolution_for_expr_or_pat(expr_id)?;
|
||||||
let variant_data = variant.variant_data(db.upcast());
|
let variant_data = variant.variant_data(db.upcast());
|
||||||
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
|
let field = FieldId { parent: variant, local_id: variant_data.field(&local_name)? };
|
||||||
let field_ty =
|
let field_ty =
|
||||||
db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
|
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(
|
pub(crate) fn resolve_record_pat_field(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
field: &ast::RecordPatField,
|
field: &ast::RecordPatField,
|
||||||
) -> Option<(Field, Type)> {
|
) -> Option<(Field, Type, GenericSubstitution)> {
|
||||||
let field_name = field.field_name()?.as_name();
|
let field_name = field.field_name()?.as_name();
|
||||||
let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
|
let record_pat = ast::RecordPat::cast(field.syntax().parent().and_then(|p| p.parent())?)?;
|
||||||
let pat_id = self.pat_id(&record_pat.into())?;
|
let pat_id = self.pat_id(&record_pat.into())?;
|
||||||
let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
|
let variant = self.infer.as_ref()?.variant_resolution_for_pat(pat_id)?;
|
||||||
let variant_data = variant.variant_data(db.upcast());
|
let variant_data = variant.variant_data(db.upcast());
|
||||||
let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
|
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 =
|
let field_ty =
|
||||||
db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
|
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(
|
pub(crate) fn resolve_macro_call(
|
||||||
|
@ -654,7 +707,7 @@ impl SourceAnalyzer {
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
path: &ast::Path,
|
path: &ast::Path,
|
||||||
) -> Option<PathResolution> {
|
) -> Option<(PathResolution, Option<GenericSubstitution>)> {
|
||||||
let parent = path.syntax().parent();
|
let parent = path.syntax().parent();
|
||||||
let parent = || parent.clone();
|
let parent = || parent.clone();
|
||||||
|
|
||||||
|
@ -664,60 +717,106 @@ impl SourceAnalyzer {
|
||||||
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
|
if let Some(path_expr) = parent().and_then(ast::PathExpr::cast) {
|
||||||
let expr_id = self.expr_id(db, &path_expr.into())?;
|
let expr_id = self.expr_id(db, &path_expr.into())?;
|
||||||
if let Some((assoc, subs)) = infer.assoc_resolutions_for_expr_or_pat(expr_id) {
|
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) => {
|
AssocItemId::FunctionId(f_in_trait) => {
|
||||||
match infer.type_of_expr_or_pat(expr_id) {
|
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) => {
|
Some(func_ty) => {
|
||||||
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
|
if let TyKind::FnDef(_fn_def, subs) = func_ty.kind(Interner) {
|
||||||
self.resolve_impl_method_or_trait_def(
|
let (fn_, subst) = self
|
||||||
db,
|
.resolve_impl_method_or_trait_def_with_subst(
|
||||||
f_in_trait,
|
db,
|
||||||
subs.clone(),
|
f_in_trait,
|
||||||
)
|
subs.clone(),
|
||||||
.into()
|
);
|
||||||
|
let subst = GenericSubstitution::new(
|
||||||
|
fn_.into(),
|
||||||
|
subst,
|
||||||
|
self.trait_environment(db),
|
||||||
|
);
|
||||||
|
(fn_.into(), subst)
|
||||||
} else {
|
} else {
|
||||||
assoc
|
let subst = GenericSubstitution::new(
|
||||||
|
f_in_trait.into(),
|
||||||
|
subs,
|
||||||
|
self.trait_environment(db),
|
||||||
|
);
|
||||||
|
(assoc, subst)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
AssocItemId::ConstId(const_id) => {
|
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)) =
|
if let Some(VariantId::EnumVariantId(variant)) =
|
||||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
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;
|
prefer_value_ns = true;
|
||||||
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
|
} else if let Some(path_pat) = parent().and_then(ast::PathPat::cast) {
|
||||||
let pat_id = self.pat_id(&path_pat.into())?;
|
let pat_id = self.pat_id(&path_pat.into())?;
|
||||||
if let Some((assoc, subs)) = infer.assoc_resolutions_for_pat(pat_id) {
|
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) => {
|
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)) =
|
if let Some(VariantId::EnumVariantId(variant)) =
|
||||||
infer.variant_resolution_for_pat(pat_id)
|
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) {
|
} else if let Some(rec_lit) = parent().and_then(ast::RecordExpr::cast) {
|
||||||
let expr_id = self.expr_id(db, &rec_lit.into())?;
|
let expr_id = self.expr_id(db, &rec_lit.into())?;
|
||||||
if let Some(VariantId::EnumVariantId(variant)) =
|
if let Some(VariantId::EnumVariantId(variant)) =
|
||||||
infer.variant_resolution_for_expr_or_pat(expr_id)
|
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 {
|
} else {
|
||||||
let record_pat = parent().and_then(ast::RecordPat::cast).map(ast::Pat::from);
|
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 pat_id = self.pat_id(&pat)?;
|
||||||
let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
|
let variant_res_for_pat = infer.variant_resolution_for_pat(pat_id);
|
||||||
if let Some(VariantId::EnumVariantId(variant)) = variant_res_for_pat {
|
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.
|
// trying to resolve foo::bar.
|
||||||
if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
|
if let Some(use_tree) = parent().and_then(ast::UseTree::cast) {
|
||||||
if use_tree.coloncolon_token().is_some() {
|
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.
|
// trying to resolve foo::bar.
|
||||||
if path.parent_path().is_some() {
|
if path.parent_path().is_some() {
|
||||||
return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) {
|
return match resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map) {
|
||||||
None if meta_path.is_some() => {
|
None if meta_path.is_some() => path
|
||||||
path.first_segment().and_then(|it| it.name_ref()).and_then(|name_ref| {
|
.first_segment()
|
||||||
|
.and_then(|it| it.name_ref())
|
||||||
|
.and_then(|name_ref| {
|
||||||
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
|
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
|
||||||
.map(PathResolution::ToolModule)
|
.map(PathResolution::ToolModule)
|
||||||
})
|
})
|
||||||
}
|
.map(|it| (it, None)),
|
||||||
res => res,
|
// 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 {
|
} else if let Some(meta_path) = meta_path {
|
||||||
// Case where we are resolving the final path segment of a path in an attribute
|
// Case where we are resolving the final path segment of a path in an attribute
|
||||||
|
@ -781,7 +889,7 @@ impl SourceAnalyzer {
|
||||||
let builtin =
|
let builtin =
|
||||||
BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
|
BuiltinAttr::by_name(db, self.resolver.krate().into(), &name_ref.text());
|
||||||
if builtin.is_some() {
|
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() {
|
if let Some(attr) = meta_path.parent_attr() {
|
||||||
|
@ -814,10 +922,13 @@ impl SourceAnalyzer {
|
||||||
{
|
{
|
||||||
if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref)
|
if let Some(idx) = helpers.position(|(name, ..)| *name == name_ref)
|
||||||
{
|
{
|
||||||
return Some(PathResolution::DeriveHelper(DeriveHelper {
|
return Some((
|
||||||
derive: *macro_id,
|
PathResolution::DeriveHelper(DeriveHelper {
|
||||||
idx: idx as u32,
|
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) {
|
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
|
// 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
|
// 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| {
|
None => path
|
||||||
ToolModule::by_name(db, self.resolver.krate().into(), &name_ref.text())
|
.first_segment()
|
||||||
.map(PathResolution::ToolModule)
|
.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().map_or(false, |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)
|
resolve_hir_path_qualifier(db, &self.resolver, &hir_path, &types_map)
|
||||||
|
.map(|it| (it, None))
|
||||||
} else {
|
} else {
|
||||||
resolve_hir_path_(
|
// Probably a type, no need to show substitutions for those.
|
||||||
|
let res = resolve_hir_path_(
|
||||||
db,
|
db,
|
||||||
&self.resolver,
|
&self.resolver,
|
||||||
&hir_path,
|
&hir_path,
|
||||||
prefer_value_ns,
|
prefer_value_ns,
|
||||||
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
|
name_hygiene(db, InFile::new(self.file_id, path.syntax())),
|
||||||
&types_map,
|
&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,
|
func: FunctionId,
|
||||||
substs: Substitution,
|
substs: Substitution,
|
||||||
) -> FunctionId {
|
) -> FunctionId {
|
||||||
let owner = match self.resolver.body_owner() {
|
self.resolve_impl_method_or_trait_def_with_subst(db, func, substs).0
|
||||||
Some(it) => it,
|
|
||||||
None => return func,
|
|
||||||
};
|
|
||||||
let env = db.trait_environment_for_body(owner);
|
|
||||||
db.lookup_impl_method(env, func, substs).0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_impl_const_or_trait_def(
|
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, substs),
|
||||||
|
};
|
||||||
|
let env = db.trait_environment_for_body(owner);
|
||||||
|
db.lookup_impl_method(env, func, substs)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_impl_const_or_trait_def_with_subst(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
const_id: ConstId,
|
const_id: ConstId,
|
||||||
subs: Substitution,
|
subs: Substitution,
|
||||||
) -> ConstId {
|
) -> (ConstId, Substitution) {
|
||||||
let owner = match self.resolver.body_owner() {
|
let owner = match self.resolver.body_owner() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return const_id,
|
None => return (const_id, subs),
|
||||||
};
|
};
|
||||||
let env = db.trait_environment_for_body(owner);
|
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(
|
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);
|
let ctx = db.lookup_intern_syntax_context(ctx);
|
||||||
HygieneId::new(ctx.opaque_and_semitransparent)
|
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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ pub(crate) fn add_turbo_fish(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
|
||||||
let ident = name_ref.ident_token()?;
|
let ident = name_ref.ident_token()?;
|
||||||
|
|
||||||
let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
|
let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
|
||||||
NameRefClass::Definition(def) => def,
|
NameRefClass::Definition(def, _) => def,
|
||||||
NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => {
|
NameRefClass::FieldShorthand { .. } | NameRefClass::ExternCrateShorthand { .. } => {
|
||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
|
||||||
ast::Expr::PathExpr(path) => {
|
ast::Expr::PathExpr(path) => {
|
||||||
let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
|
let name_ref = path.syntax().descendants().find_map(ast::NameRef::cast)?;
|
||||||
match NameRefClass::classify(&ctx.sema, &name_ref)? {
|
match NameRefClass::classify(&ctx.sema, &name_ref)? {
|
||||||
NameRefClass::Definition(Definition::Local(local)) => {
|
NameRefClass::Definition(Definition::Local(local), _) => {
|
||||||
let source =
|
let source =
|
||||||
local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name());
|
local.sources(ctx.db()).into_iter().map(|x| x.into_ident_pat()?.name());
|
||||||
source.collect()
|
source.collect()
|
||||||
|
|
|
@ -163,8 +163,8 @@ fn edit_struct_references(
|
||||||
// this also includes method calls like Foo::new(42), we should skip them
|
// this also includes method calls like Foo::new(42), we should skip them
|
||||||
if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
|
if let Some(name_ref) = path.segment().and_then(|s| s.name_ref()) {
|
||||||
match NameRefClass::classify(&ctx.sema, &name_ref) {
|
match NameRefClass::classify(&ctx.sema, &name_ref) {
|
||||||
Some(NameRefClass::Definition(Definition::SelfType(_))) => {},
|
Some(NameRefClass::Definition(Definition::SelfType(_), _)) => {},
|
||||||
Some(NameRefClass::Definition(def)) if def == strukt_def => {},
|
Some(NameRefClass::Definition(def, _)) if def == strukt_def => {},
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,6 +275,7 @@ fn find_imported_defs(ctx: &AssistContext<'_>, star: SyntaxToken) -> Option<Vec<
|
||||||
| Definition::Static(_)
|
| Definition::Static(_)
|
||||||
| Definition::Trait(_)
|
| Definition::Trait(_)
|
||||||
| Definition::TypeAlias(_)),
|
| Definition::TypeAlias(_)),
|
||||||
|
_,
|
||||||
) => Some(def),
|
) => Some(def),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
|
|
|
@ -800,8 +800,8 @@ impl FunctionBody {
|
||||||
let local_ref =
|
let local_ref =
|
||||||
match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
|
match name_ref.and_then(|name_ref| NameRefClass::classify(sema, &name_ref)) {
|
||||||
Some(
|
Some(
|
||||||
NameRefClass::Definition(Definition::Local(local_ref))
|
NameRefClass::Definition(Definition::Local(local_ref), _)
|
||||||
| NameRefClass::FieldShorthand { local_ref, field_ref: _ },
|
| NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ },
|
||||||
) => local_ref,
|
) => local_ref,
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
|
@ -425,7 +425,9 @@ impl Module {
|
||||||
})
|
})
|
||||||
} else if let Some(name_ref) = ast::NameRef::cast(x) {
|
} else if let Some(name_ref) = ast::NameRef::cast(x) {
|
||||||
NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc {
|
NameRefClass::classify(&ctx.sema, &name_ref).and_then(|nc| match nc {
|
||||||
NameRefClass::Definition(def) => Some((name_ref.syntax().clone(), def)),
|
NameRefClass::Definition(def, _) => {
|
||||||
|
Some((name_ref.syntax().clone(), def))
|
||||||
|
}
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -64,7 +64,7 @@ pub(crate) fn generate_constant(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
|
||||||
let name_ref_value = name_ref?;
|
let name_ref_value = name_ref?;
|
||||||
let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value);
|
let name_ref_class = NameRefClass::classify(&ctx.sema, &name_ref_value);
|
||||||
match name_ref_class {
|
match name_ref_class {
|
||||||
Some(NameRefClass::Definition(Definition::Module(m))) => {
|
Some(NameRefClass::Definition(Definition::Module(m), _)) => {
|
||||||
if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) {
|
if !m.visibility(ctx.sema.db).is_visible_from(ctx.sema.db, constant_module.into()) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1077,7 +1077,7 @@ fn fn_arg_name(sema: &Semantics<'_, RootDatabase>, arg_expr: &ast::Expr) -> Stri
|
||||||
.filter_map(ast::NameRef::cast)
|
.filter_map(ast::NameRef::cast)
|
||||||
.filter(|name| name.ident_token().is_some())
|
.filter(|name| name.ident_token().is_some())
|
||||||
.last()?;
|
.last()?;
|
||||||
if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_))) =
|
if let Some(NameRefClass::Definition(Definition::Const(_) | Definition::Static(_), _)) =
|
||||||
NameRefClass::classify(sema, &name_ref)
|
NameRefClass::classify(sema, &name_ref)
|
||||||
{
|
{
|
||||||
return Some(name_ref.to_string().to_lowercase());
|
return Some(name_ref.to_string().to_lowercase());
|
||||||
|
|
|
@ -13,9 +13,10 @@ use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
||||||
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
||||||
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
|
Function, GenericParam, GenericSubstitution, HasVisibility, HirDisplay, Impl, InlineAsmOperand,
|
||||||
Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
|
Label, Local, Macro, Module, ModuleDef, Name, PathResolution, Semantics, Static,
|
||||||
Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
StaticLifetime, Struct, ToolModule, Trait, TraitAlias, TupleField, TypeAlias, Variant,
|
||||||
|
VariantDef, Visibility,
|
||||||
};
|
};
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
use stdx::{format_to, impl_from};
|
use stdx::{format_to, impl_from};
|
||||||
|
@ -359,24 +360,32 @@ impl IdentClass {
|
||||||
.or_else(|| NameClass::classify_lifetime(sema, lifetime).map(IdentClass::NameClass))
|
.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();
|
let mut res = ArrayVec::new();
|
||||||
match self {
|
match self {
|
||||||
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
|
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
|
||||||
res.push(it)
|
res.push((it, None))
|
||||||
}
|
}
|
||||||
IdentClass::NameClass(NameClass::PatFieldShorthand { local_def, field_ref }) => {
|
IdentClass::NameClass(NameClass::PatFieldShorthand {
|
||||||
res.push(Definition::Local(local_def));
|
local_def,
|
||||||
res.push(Definition::Field(field_ref));
|
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::Definition(it, subst)) => res.push((it, subst)),
|
||||||
IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
|
IdentClass::NameRefClass(NameRefClass::FieldShorthand {
|
||||||
res.push(Definition::Local(local_ref));
|
local_ref,
|
||||||
res.push(Definition::Field(field_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 }) => {
|
IdentClass::NameRefClass(NameRefClass::ExternCrateShorthand { decl, krate }) => {
|
||||||
res.push(Definition::ExternCrateDecl(decl));
|
res.push((Definition::ExternCrateDecl(decl), None));
|
||||||
res.push(Definition::Module(krate.root_module()));
|
res.push((Definition::Module(krate.root_module()), None));
|
||||||
}
|
}
|
||||||
IdentClass::Operator(
|
IdentClass::Operator(
|
||||||
OperatorClass::Await(func)
|
OperatorClass::Await(func)
|
||||||
|
@ -384,9 +393,9 @@ impl IdentClass {
|
||||||
| OperatorClass::Bin(func)
|
| OperatorClass::Bin(func)
|
||||||
| OperatorClass::Index(func)
|
| OperatorClass::Index(func)
|
||||||
| OperatorClass::Try(func),
|
| OperatorClass::Try(func),
|
||||||
) => res.push(Definition::Function(func)),
|
) => res.push((Definition::Function(func), None)),
|
||||||
IdentClass::Operator(OperatorClass::Range(struct0)) => {
|
IdentClass::Operator(OperatorClass::Range(struct0)) => {
|
||||||
res.push(Definition::Adt(Adt::Struct(struct0)))
|
res.push((Definition::Adt(Adt::Struct(struct0)), None))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
|
@ -398,12 +407,20 @@ impl IdentClass {
|
||||||
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
|
IdentClass::NameClass(NameClass::Definition(it) | NameClass::ConstReference(it)) => {
|
||||||
res.push(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::Local(local_def));
|
||||||
res.push(Definition::Field(field_ref));
|
res.push(Definition::Field(field_ref));
|
||||||
}
|
}
|
||||||
IdentClass::NameRefClass(NameRefClass::Definition(it)) => res.push(it),
|
IdentClass::NameRefClass(NameRefClass::Definition(it, _)) => res.push(it),
|
||||||
IdentClass::NameRefClass(NameRefClass::FieldShorthand { local_ref, field_ref }) => {
|
IdentClass::NameRefClass(NameRefClass::FieldShorthand {
|
||||||
|
local_ref,
|
||||||
|
field_ref,
|
||||||
|
adt_subst: _,
|
||||||
|
}) => {
|
||||||
res.push(Definition::Local(local_ref));
|
res.push(Definition::Local(local_ref));
|
||||||
res.push(Definition::Field(field_ref));
|
res.push(Definition::Field(field_ref));
|
||||||
}
|
}
|
||||||
|
@ -437,6 +454,7 @@ pub enum NameClass {
|
||||||
PatFieldShorthand {
|
PatFieldShorthand {
|
||||||
local_def: Local,
|
local_def: Local,
|
||||||
field_ref: Field,
|
field_ref: Field,
|
||||||
|
adt_subst: GenericSubstitution,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -446,7 +464,7 @@ impl NameClass {
|
||||||
let res = match self {
|
let res = match self {
|
||||||
NameClass::Definition(it) => it,
|
NameClass::Definition(it) => it,
|
||||||
NameClass::ConstReference(_) => return None,
|
NameClass::ConstReference(_) => return None,
|
||||||
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
|
||||||
Definition::Local(local_def)
|
Definition::Local(local_def)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -517,10 +535,13 @@ impl NameClass {
|
||||||
let pat_parent = ident_pat.syntax().parent();
|
let pat_parent = ident_pat.syntax().parent();
|
||||||
if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
|
if let Some(record_pat_field) = pat_parent.and_then(ast::RecordPatField::cast) {
|
||||||
if record_pat_field.name_ref().is_none() {
|
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 {
|
return Some(NameClass::PatFieldShorthand {
|
||||||
local_def: local,
|
local_def: local,
|
||||||
field_ref: field,
|
field_ref: field,
|
||||||
|
adt_subst,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -629,10 +650,11 @@ impl OperatorClass {
|
||||||
/// reference to point to two different defs.
|
/// reference to point to two different defs.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum NameRefClass {
|
pub enum NameRefClass {
|
||||||
Definition(Definition),
|
Definition(Definition, Option<GenericSubstitution>),
|
||||||
FieldShorthand {
|
FieldShorthand {
|
||||||
local_ref: Local,
|
local_ref: Local,
|
||||||
field_ref: Field,
|
field_ref: Field,
|
||||||
|
adt_subst: GenericSubstitution,
|
||||||
},
|
},
|
||||||
/// The specific situation where we have an extern crate decl without a rename
|
/// The specific situation where we have an extern crate decl without a rename
|
||||||
/// Here we have both a declaration and a reference.
|
/// Here we have both a declaration and a reference.
|
||||||
|
@ -657,12 +679,16 @@ impl NameRefClass {
|
||||||
let parent = name_ref.syntax().parent()?;
|
let parent = name_ref.syntax().parent()?;
|
||||||
|
|
||||||
if let Some(record_field) = ast::RecordExprField::for_field_name(name_ref) {
|
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 {
|
let res = match local {
|
||||||
None => NameRefClass::Definition(Definition::Field(field)),
|
None => NameRefClass::Definition(Definition::Field(field), Some(adt_subst)),
|
||||||
Some(local) => {
|
Some(local) => NameRefClass::FieldShorthand {
|
||||||
NameRefClass::FieldShorthand { field_ref: field, local_ref: local }
|
field_ref: field,
|
||||||
}
|
local_ref: local,
|
||||||
|
adt_subst,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
|
@ -674,44 +700,43 @@ impl NameRefClass {
|
||||||
// Only use this to resolve to macro calls for last segments as qualifiers resolve
|
// Only use this to resolve to macro calls for last segments as qualifiers resolve
|
||||||
// to modules below.
|
// to modules below.
|
||||||
if let Some(macro_def) = sema.resolve_macro_call(¯o_call) {
|
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_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::MethodCallExpr(method_call) => {
|
ast::MethodCallExpr(method_call) => {
|
||||||
sema.resolve_method_call_fallback(&method_call)
|
sema.resolve_method_call_fallback(&method_call)
|
||||||
.map(|it| {
|
.map(|(def, subst)| {
|
||||||
it.map_left(Definition::Function)
|
match def {
|
||||||
.map_right(Definition::Field)
|
Either::Left(def) => NameRefClass::Definition(def.into(), subst),
|
||||||
.either(NameRefClass::Definition, NameRefClass::Definition)
|
Either::Right(def) => NameRefClass::Definition(def.into(), subst),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::FieldExpr(field_expr) => {
|
ast::FieldExpr(field_expr) => {
|
||||||
sema.resolve_field_fallback(&field_expr)
|
sema.resolve_field_fallback(&field_expr)
|
||||||
.map(|it| {
|
.map(|(def, subst)| {
|
||||||
NameRefClass::Definition(match it {
|
match def {
|
||||||
Either::Left(Either::Left(field)) => Definition::Field(field),
|
Either::Left(Either::Left(def)) => NameRefClass::Definition(def.into(), subst),
|
||||||
Either::Left(Either::Right(field)) => Definition::TupleField(field),
|
Either::Left(Either::Right(def)) => NameRefClass::Definition(Definition::TupleField(def), subst),
|
||||||
Either::Right(fun) => Definition::Function(fun),
|
Either::Right(def) => NameRefClass::Definition(def.into(), subst),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
|
||||||
},
|
},
|
||||||
ast::RecordPatField(record_pat_field) => {
|
ast::RecordPatField(record_pat_field) => {
|
||||||
sema.resolve_record_pat_field(&record_pat_field)
|
sema.resolve_record_pat_field_with_subst(&record_pat_field)
|
||||||
.map(|(field, ..)|field)
|
.map(|(field, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
|
||||||
.map(Definition::Field)
|
|
||||||
.map(NameRefClass::Definition)
|
|
||||||
},
|
},
|
||||||
ast::RecordExprField(record_expr_field) => {
|
ast::RecordExprField(record_expr_field) => {
|
||||||
sema.resolve_record_field(&record_expr_field)
|
sema.resolve_record_field_with_substitution(&record_expr_field)
|
||||||
.map(|(field, ..)|field)
|
.map(|(field, _, _, subst)| NameRefClass::Definition(Definition::Field(field), Some(subst)))
|
||||||
.map(Definition::Field)
|
|
||||||
.map(NameRefClass::Definition)
|
|
||||||
},
|
},
|
||||||
ast::AssocTypeArg(_) => {
|
ast::AssocTypeArg(_) => {
|
||||||
// `Trait<Assoc = Ty>`
|
// `Trait<Assoc = Ty>`
|
||||||
|
@ -728,28 +753,30 @@ impl NameRefClass {
|
||||||
})
|
})
|
||||||
.find(|alias| alias.name(sema.db).eq_ident(name_ref.text().as_str()))
|
.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
|
None
|
||||||
},
|
},
|
||||||
ast::UseBoundGenericArgs(_) => {
|
ast::UseBoundGenericArgs(_) => {
|
||||||
|
// No substitution, this can only occur in type position.
|
||||||
sema.resolve_use_type_arg(name_ref)
|
sema.resolve_use_type_arg(name_ref)
|
||||||
.map(GenericParam::TypeParam)
|
.map(GenericParam::TypeParam)
|
||||||
.map(Definition::GenericParam)
|
.map(Definition::GenericParam)
|
||||||
.map(NameRefClass::Definition)
|
.map(|it| NameRefClass::Definition(it, None))
|
||||||
},
|
},
|
||||||
ast::ExternCrate(extern_crate_ast) => {
|
ast::ExternCrate(extern_crate_ast) => {
|
||||||
let extern_crate = sema.to_def(&extern_crate_ast)?;
|
let extern_crate = sema.to_def(&extern_crate_ast)?;
|
||||||
let krate = extern_crate.resolved_crate(sema.db)?;
|
let krate = extern_crate.resolved_crate(sema.db)?;
|
||||||
Some(if extern_crate_ast.rename().is_some() {
|
Some(if extern_crate_ast.rename().is_some() {
|
||||||
NameRefClass::Definition(Definition::Module(krate.root_module()))
|
NameRefClass::Definition(Definition::Module(krate.root_module()), None)
|
||||||
} else {
|
} else {
|
||||||
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
|
NameRefClass::ExternCrateShorthand { krate, decl: extern_crate }
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
ast::AsmRegSpec(_) => {
|
ast::AsmRegSpec(_) => {
|
||||||
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(())))
|
Some(NameRefClass::Definition(Definition::InlineAsmRegOrRegClass(()), None))
|
||||||
},
|
},
|
||||||
_ => None
|
_ => None
|
||||||
}
|
}
|
||||||
|
@ -762,13 +789,17 @@ impl NameRefClass {
|
||||||
) -> Option<NameRefClass> {
|
) -> Option<NameRefClass> {
|
||||||
let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
|
let _p = tracing::info_span!("NameRefClass::classify_lifetime", ?lifetime).entered();
|
||||||
if lifetime.text() == "'static" {
|
if lifetime.text() == "'static" {
|
||||||
return Some(NameRefClass::Definition(Definition::BuiltinLifetime(StaticLifetime)));
|
return Some(NameRefClass::Definition(
|
||||||
|
Definition::BuiltinLifetime(StaticLifetime),
|
||||||
|
None,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
let parent = lifetime.syntax().parent()?;
|
let parent = lifetime.syntax().parent()?;
|
||||||
match parent.kind() {
|
match parent.kind() {
|
||||||
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => {
|
SyntaxKind::BREAK_EXPR | SyntaxKind::CONTINUE_EXPR => sema
|
||||||
sema.resolve_label(lifetime).map(Definition::Label).map(NameRefClass::Definition)
|
.resolve_label(lifetime)
|
||||||
}
|
.map(Definition::Label)
|
||||||
|
.map(|it| NameRefClass::Definition(it, None)),
|
||||||
SyntaxKind::LIFETIME_ARG
|
SyntaxKind::LIFETIME_ARG
|
||||||
| SyntaxKind::USE_BOUND_GENERIC_ARGS
|
| SyntaxKind::USE_BOUND_GENERIC_ARGS
|
||||||
| SyntaxKind::SELF_PARAM
|
| SyntaxKind::SELF_PARAM
|
||||||
|
@ -778,7 +809,7 @@ impl NameRefClass {
|
||||||
.resolve_lifetime_param(lifetime)
|
.resolve_lifetime_param(lifetime)
|
||||||
.map(GenericParam::LifetimeParam)
|
.map(GenericParam::LifetimeParam)
|
||||||
.map(Definition::GenericParam)
|
.map(Definition::GenericParam)
|
||||||
.map(NameRefClass::Definition),
|
.map(|it| NameRefClass::Definition(it, None)),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1081,7 +1081,7 @@ impl<'a> FindUsages<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match NameRefClass::classify(self.sema, name_ref) {
|
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)) =>
|
if ty_eq(impl_.self_ty(self.sema.db)) =>
|
||||||
{
|
{
|
||||||
let FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
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,
|
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match NameRefClass::classify(self.sema, name_ref) {
|
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 FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||||
let category = if is_name_ref_in_import(name_ref) {
|
let category = if is_name_ref_in_import(name_ref) {
|
||||||
ReferenceCategory::IMPORT
|
ReferenceCategory::IMPORT
|
||||||
|
@ -1147,7 +1147,7 @@ impl<'a> FindUsages<'a> {
|
||||||
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match NameRefClass::classify_lifetime(self.sema, lifetime) {
|
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 FileRange { file_id, range } = self.sema.original_range(lifetime.syntax());
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
range,
|
range,
|
||||||
|
@ -1166,7 +1166,7 @@ impl<'a> FindUsages<'a> {
|
||||||
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match NameRefClass::classify(self.sema, name_ref) {
|
match NameRefClass::classify(self.sema, name_ref) {
|
||||||
Some(NameRefClass::Definition(def))
|
Some(NameRefClass::Definition(def, _))
|
||||||
if self.def == 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
|
// 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(_)))
|
|| 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
|
// 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
|
// 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()
|
if self.assoc_item_container.is_some()
|
||||||
&& matches!(self.def, Definition::TypeAlias(_))
|
&& matches!(self.def, Definition::TypeAlias(_))
|
||||||
&& convert_to_def_in_trait(self.sema.db, def)
|
&& convert_to_def_in_trait(self.sema.db, def)
|
||||||
|
@ -1196,7 +1196,7 @@ impl<'a> FindUsages<'a> {
|
||||||
};
|
};
|
||||||
sink(file_id, reference)
|
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) {
|
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 FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||||
let reference = FileReference {
|
let reference = FileReference {
|
||||||
|
@ -1209,7 +1209,11 @@ impl<'a> FindUsages<'a> {
|
||||||
false
|
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 FileRange { file_id, range } = self.sema.original_range(name_ref.syntax());
|
||||||
|
|
||||||
let field = Definition::Field(field);
|
let field = Definition::Field(field);
|
||||||
|
@ -1240,7 +1244,7 @@ impl<'a> FindUsages<'a> {
|
||||||
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
sink: &mut dyn FnMut(EditionedFileId, FileReference) -> bool,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
match NameClass::classify(self.sema, name) {
|
match NameClass::classify(self.sema, name) {
|
||||||
Some(NameClass::PatFieldShorthand { local_def: _, field_ref })
|
Some(NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ })
|
||||||
if matches!(
|
if matches!(
|
||||||
self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
|
self.def, Definition::Field(_) if Definition::Field(field_ref) == self.def
|
||||||
) =>
|
) =>
|
||||||
|
|
|
@ -47,7 +47,7 @@ pub(crate) fn incoming_calls(
|
||||||
.find_nodes_at_offset_with_descend(file, offset)
|
.find_nodes_at_offset_with_descend(file, offset)
|
||||||
.filter_map(move |node| match node {
|
.filter_map(move |node| match node {
|
||||||
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
|
ast::NameLike::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
|
||||||
NameRefClass::Definition(def @ Definition::Function(_)) => Some(def),
|
NameRefClass::Definition(def @ Definition::Function(_), _) => Some(def),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
|
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
|
||||||
|
|
|
@ -147,8 +147,8 @@ pub(crate) fn external_docs(
|
||||||
let definition = match_ast! {
|
let definition = match_ast! {
|
||||||
match node {
|
match node {
|
||||||
ast::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
|
ast::NameRef(name_ref) => match NameRefClass::classify(sema, &name_ref)? {
|
||||||
NameRefClass::Definition(def) => def,
|
NameRefClass::Definition(def, _) => def,
|
||||||
NameRefClass::FieldShorthand { local_ref: _, field_ref } => {
|
NameRefClass::FieldShorthand { local_ref: _, field_ref, adt_subst: _ } => {
|
||||||
Definition::Field(field_ref)
|
Definition::Field(field_ref)
|
||||||
}
|
}
|
||||||
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
||||||
|
@ -157,7 +157,7 @@ pub(crate) fn external_docs(
|
||||||
},
|
},
|
||||||
ast::Name(name) => match NameClass::classify(sema, &name)? {
|
ast::Name(name) => match NameClass::classify(sema, &name)? {
|
||||||
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
NameClass::PatFieldShorthand { local_def: _, field_ref } => Definition::Field(field_ref),
|
NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => Definition::Field(field_ref),
|
||||||
},
|
},
|
||||||
_ => return None
|
_ => return None
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub(crate) fn goto_declaration(
|
||||||
let def = match_ast! {
|
let def = match_ast! {
|
||||||
match parent {
|
match parent {
|
||||||
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
|
ast::NameRef(name_ref) => match NameRefClass::classify(&sema, &name_ref)? {
|
||||||
NameRefClass::Definition(it) => Some(it),
|
NameRefClass::Definition(it, _) => Some(it),
|
||||||
NameRefClass::FieldShorthand { field_ref, .. } =>
|
NameRefClass::FieldShorthand { field_ref, .. } =>
|
||||||
return field_ref.try_to_nav(db),
|
return field_ref.try_to_nav(db),
|
||||||
NameRefClass::ExternCrateShorthand { decl, .. } =>
|
NameRefClass::ExternCrateShorthand { decl, .. } =>
|
||||||
|
|
|
@ -103,7 +103,7 @@ pub(crate) fn goto_definition(
|
||||||
IdentClass::classify_node(sema, &parent)?
|
IdentClass::classify_node(sema, &parent)?
|
||||||
.definitions()
|
.definitions()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(|def| {
|
.flat_map(|(def, _)| {
|
||||||
if let Definition::ExternCrateDecl(crate_def) = def {
|
if let Definition::ExternCrateDecl(crate_def) = def {
|
||||||
return crate_def
|
return crate_def
|
||||||
.resolved_crate(db)
|
.resolved_crate(db)
|
||||||
|
|
|
@ -48,7 +48,7 @@ pub(crate) fn goto_implementation(
|
||||||
}
|
}
|
||||||
ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
|
ast::NameLike::NameRef(name_ref) => NameRefClass::classify(&sema, name_ref)
|
||||||
.and_then(|class| match class {
|
.and_then(|class| match class {
|
||||||
NameRefClass::Definition(def) => Some(def),
|
NameRefClass::Definition(def, _) => Some(def),
|
||||||
NameRefClass::FieldShorthand { .. }
|
NameRefClass::FieldShorthand { .. }
|
||||||
| NameRefClass::ExternCrateShorthand { .. } => None,
|
| NameRefClass::ExternCrateShorthand { .. } => None,
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -6,7 +6,7 @@ mod tests;
|
||||||
use std::{iter, ops::Not};
|
use std::{iter, ops::Not};
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{db::DefDatabase, HasCrate, HasSource, LangItem, Semantics};
|
use hir::{db::DefDatabase, GenericSubstitution, HasCrate, HasSource, LangItem, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
|
defs::{Definition, IdentClass, NameRefClass, OperatorClass},
|
||||||
famous_defs::FamousDefs,
|
famous_defs::FamousDefs,
|
||||||
|
@ -35,6 +35,14 @@ pub struct HoverConfig {
|
||||||
pub max_trait_assoc_items_count: Option<usize>,
|
pub max_trait_assoc_items_count: Option<usize>,
|
||||||
pub max_fields_count: Option<usize>,
|
pub max_fields_count: Option<usize>,
|
||||||
pub max_enum_variants_count: Option<usize>,
|
pub max_enum_variants_count: Option<usize>,
|
||||||
|
pub max_subst_ty_len: SubstTyLen,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum SubstTyLen {
|
||||||
|
Unlimited,
|
||||||
|
LimitTo(usize),
|
||||||
|
Hide,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
@ -158,7 +166,8 @@ fn hover_offset(
|
||||||
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
|
if let Some(doc_comment) = token_as_doc_comment(&original_token) {
|
||||||
cov_mark::hit!(no_highlight_on_comment_hover);
|
cov_mark::hit!(no_highlight_on_comment_hover);
|
||||||
return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
|
return doc_comment.get_definition_with_descend_at(sema, offset, |def, node, range| {
|
||||||
let res = hover_for_definition(sema, file_id, def, &node, None, false, config, edition);
|
let res =
|
||||||
|
hover_for_definition(sema, file_id, def, None, &node, None, false, config, edition);
|
||||||
Some(RangeInfo::new(range, res))
|
Some(RangeInfo::new(range, res))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -170,6 +179,7 @@ fn hover_offset(
|
||||||
sema,
|
sema,
|
||||||
file_id,
|
file_id,
|
||||||
Definition::from(resolution?),
|
Definition::from(resolution?),
|
||||||
|
None,
|
||||||
&original_token.parent()?,
|
&original_token.parent()?,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
@ -217,7 +227,7 @@ fn hover_offset(
|
||||||
{
|
{
|
||||||
if let Some(macro_) = sema.resolve_macro_call(¯o_call) {
|
if let Some(macro_) = sema.resolve_macro_call(¯o_call) {
|
||||||
break 'a vec![(
|
break 'a vec![(
|
||||||
Definition::Macro(macro_),
|
(Definition::Macro(macro_), None),
|
||||||
sema.resolve_macro_call_arm(¯o_call),
|
sema.resolve_macro_call_arm(¯o_call),
|
||||||
false,
|
false,
|
||||||
node,
|
node,
|
||||||
|
@ -236,7 +246,7 @@ fn hover_offset(
|
||||||
decl,
|
decl,
|
||||||
..
|
..
|
||||||
}) => {
|
}) => {
|
||||||
vec![(Definition::ExternCrateDecl(decl), None, false, node)]
|
vec![((Definition::ExternCrateDecl(decl), None), None, false, node)]
|
||||||
}
|
}
|
||||||
|
|
||||||
class => {
|
class => {
|
||||||
|
@ -252,12 +262,13 @@ fn hover_offset(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.unique_by(|&(def, _, _, _)| def)
|
.unique_by(|&((def, _), _, _, _)| def)
|
||||||
.map(|(def, macro_arm, hovered_definition, node)| {
|
.map(|((def, subst), macro_arm, hovered_definition, node)| {
|
||||||
hover_for_definition(
|
hover_for_definition(
|
||||||
sema,
|
sema,
|
||||||
file_id,
|
file_id,
|
||||||
def,
|
def,
|
||||||
|
subst,
|
||||||
&node,
|
&node,
|
||||||
macro_arm,
|
macro_arm,
|
||||||
hovered_definition,
|
hovered_definition,
|
||||||
|
@ -381,6 +392,7 @@ pub(crate) fn hover_for_definition(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
file_id: FileId,
|
file_id: FileId,
|
||||||
def: Definition,
|
def: Definition,
|
||||||
|
subst: Option<GenericSubstitution>,
|
||||||
scope_node: &SyntaxNode,
|
scope_node: &SyntaxNode,
|
||||||
macro_arm: Option<u32>,
|
macro_arm: Option<u32>,
|
||||||
hovered_definition: bool,
|
hovered_definition: bool,
|
||||||
|
@ -408,6 +420,7 @@ pub(crate) fn hover_for_definition(
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
|
let notable_traits = def_ty.map(|ty| notable_traits(db, &ty)).unwrap_or_default();
|
||||||
|
let subst_types = subst.map(|subst| subst.types(db));
|
||||||
|
|
||||||
let markup = render::definition(
|
let markup = render::definition(
|
||||||
sema.db,
|
sema.db,
|
||||||
|
@ -416,6 +429,7 @@ pub(crate) fn hover_for_definition(
|
||||||
¬able_traits,
|
¬able_traits,
|
||||||
macro_arm,
|
macro_arm,
|
||||||
hovered_definition,
|
hovered_definition,
|
||||||
|
subst_types,
|
||||||
config,
|
config,
|
||||||
edition,
|
edition,
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,7 +5,7 @@ use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind,
|
db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind,
|
||||||
DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
|
DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
|
||||||
MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo,
|
MethodViolationCode, Name, Semantics, Symbol, Trait, Type, TypeInfo,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::SourceDatabase,
|
base_db::SourceDatabase,
|
||||||
|
@ -27,7 +27,7 @@ use syntax::{algo, ast, match_ast, AstNode, AstToken, Direction, SyntaxToken, T}
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
doc_links::{remove_links, rewrite_links},
|
doc_links::{remove_links, rewrite_links},
|
||||||
hover::{notable_traits, walk_and_push_ty},
|
hover::{notable_traits, walk_and_push_ty, SubstTyLen},
|
||||||
interpret::render_const_eval_error,
|
interpret::render_const_eval_error,
|
||||||
HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
|
HoverAction, HoverConfig, HoverResult, Markup, MemoryLayoutHoverConfig,
|
||||||
MemoryLayoutHoverRenderKind,
|
MemoryLayoutHoverRenderKind,
|
||||||
|
@ -274,7 +274,7 @@ pub(super) fn keyword(
|
||||||
let markup = process_markup(
|
let markup = process_markup(
|
||||||
sema.db,
|
sema.db,
|
||||||
Definition::Module(doc_owner),
|
Definition::Module(doc_owner),
|
||||||
&markup(Some(docs.into()), description, None, None),
|
&markup(Some(docs.into()), description, None, None, String::new()),
|
||||||
config,
|
config,
|
||||||
);
|
);
|
||||||
Some(HoverResult { markup, actions })
|
Some(HoverResult { markup, actions })
|
||||||
|
@ -421,6 +421,7 @@ pub(super) fn definition(
|
||||||
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
|
notable_traits: &[(Trait, Vec<(Option<Type>, Name)>)],
|
||||||
macro_arm: Option<u32>,
|
macro_arm: Option<u32>,
|
||||||
hovered_definition: bool,
|
hovered_definition: bool,
|
||||||
|
subst_types: Option<Vec<(Symbol, Type)>>,
|
||||||
config: &HoverConfig,
|
config: &HoverConfig,
|
||||||
edition: Edition,
|
edition: Edition,
|
||||||
) -> Markup {
|
) -> Markup {
|
||||||
|
@ -604,7 +605,38 @@ pub(super) fn definition(
|
||||||
desc.push_str(&value);
|
desc.push_str(&value);
|
||||||
}
|
}
|
||||||
|
|
||||||
markup(docs.map(Into::into), desc, extra.is_empty().not().then_some(extra), mod_path)
|
let subst_types = match config.max_subst_ty_len {
|
||||||
|
SubstTyLen::Hide => String::new(),
|
||||||
|
SubstTyLen::LimitTo(_) | SubstTyLen::Unlimited => {
|
||||||
|
let limit = if let SubstTyLen::LimitTo(limit) = config.max_subst_ty_len {
|
||||||
|
Some(limit)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
subst_types
|
||||||
|
.map(|subst_type| {
|
||||||
|
subst_type
|
||||||
|
.iter()
|
||||||
|
.filter(|(_, ty)| !ty.is_unknown())
|
||||||
|
.format_with(", ", |(name, ty), fmt| {
|
||||||
|
fmt(&format_args!(
|
||||||
|
"`{name}` = `{}`",
|
||||||
|
ty.display_truncated(db, limit, edition)
|
||||||
|
))
|
||||||
|
})
|
||||||
|
.to_string()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
markup(
|
||||||
|
docs.map(Into::into),
|
||||||
|
desc,
|
||||||
|
extra.is_empty().not().then_some(extra),
|
||||||
|
mod_path,
|
||||||
|
subst_types,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn literal(
|
pub(super) fn literal(
|
||||||
|
@ -872,6 +904,7 @@ fn markup(
|
||||||
rust: String,
|
rust: String,
|
||||||
extra: Option<String>,
|
extra: Option<String>,
|
||||||
mod_path: Option<String>,
|
mod_path: Option<String>,
|
||||||
|
subst_types: String,
|
||||||
) -> Markup {
|
) -> Markup {
|
||||||
let mut buf = String::new();
|
let mut buf = String::new();
|
||||||
|
|
||||||
|
@ -886,6 +919,10 @@ fn markup(
|
||||||
buf.push_str(&extra);
|
buf.push_str(&extra);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !subst_types.is_empty() {
|
||||||
|
format_to!(buf, "\n___\n{subst_types}");
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(doc) = docs {
|
if let Some(doc) = docs {
|
||||||
format_to!(buf, "\n___\n\n{}", doc);
|
format_to!(buf, "\n___\n\n{}", doc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ const HOVER_BASE_CONFIG: HoverConfig = HoverConfig {
|
||||||
max_trait_assoc_items_count: None,
|
max_trait_assoc_items_count: None,
|
||||||
max_fields_count: Some(5),
|
max_fields_count: Some(5),
|
||||||
max_enum_variants_count: Some(5),
|
max_enum_variants_count: Some(5),
|
||||||
|
max_subst_ty_len: super::SubstTyLen::Unlimited,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn check_hover_no_result(ra_fixture: &str) {
|
fn check_hover_no_result(ra_fixture: &str) {
|
||||||
|
@ -5176,6 +5177,10 @@ fn main() {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
`Self` = `()`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
false
|
false
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -5208,6 +5213,10 @@ fn main() {
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
`Self` = `i32`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
false
|
false
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -9501,3 +9510,409 @@ fn main() {
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_fn() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T>(T);
|
||||||
|
impl<T> Foo<T> {
|
||||||
|
fn foo<U>(v: T, u: U) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo::fo$0o(123, false);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl<T> Foo<T>
|
||||||
|
fn foo<U>(v: T, u: U)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`, `U` = `bool`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
fn foo<T>(v: T) {}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
fo$0o(123);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn foo<T>(v: T)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_record_constructor() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = $0Foo { field: 123 };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*Foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo<T> {
|
||||||
|
field: T,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo { field: 123 };
|
||||||
|
let $0Foo { field: _ } = v;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*Foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
struct Foo<T> {
|
||||||
|
field: T,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_method_call() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T>(T);
|
||||||
|
|
||||||
|
impl<U> Foo<U> {
|
||||||
|
fn bar<T>(self, v: T) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn baz() {
|
||||||
|
Foo(123).bar$0("hello");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*bar*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl<U> Foo<U>
|
||||||
|
fn bar<T>(self, v: T)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`U` = `i32`, `T` = `&str`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_type_alias_do_not_work() {
|
||||||
|
// It is very hard to support subst for type aliases properly in all places because they are eagerly evaluated.
|
||||||
|
// We can show the user the subst for the underlying type instead but that'll be very confusing.
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T, U> { a: T, b: U }
|
||||||
|
type Alias<T> = Foo<T, i32>;
|
||||||
|
|
||||||
|
fn foo() {
|
||||||
|
let _ = Alias$0 { a: true, b: 123 };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*Alias*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
type Alias<T> = Foo<T, i32>
|
||||||
|
```
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_self() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
trait Trait<T> {
|
||||||
|
fn foo<U>(&self, v: U) {}
|
||||||
|
}
|
||||||
|
struct Struct<T>(T);
|
||||||
|
impl<T> Trait<i64> for Struct<T> {}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Struct(123).foo$0(true);
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Trait
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
trait Trait<T>
|
||||||
|
fn foo<U>(&self, v: U)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`Self` = `Struct<i32>`, `T` = `i64`, `U` = `bool`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_with_lifetimes_and_consts() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<'a, const N: usize, T>(&[T; N]);
|
||||||
|
|
||||||
|
impl<'a, T, const N: usize> Foo<'a, N, T> {
|
||||||
|
fn foo<'b, const Z: u32, U>(&self, v: U) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
Foo(&[1i8]).fo$0o::<456, _>("");
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*foo*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
impl<'a, T, const N: usize> Foo<'a, N, T>
|
||||||
|
fn foo<'b, const Z: u32, U>(&self, v: U)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i8`, `U` = `&str`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn subst_field() {
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo { $0field: 123 };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*field*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
field: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let field = 123;
|
||||||
|
let v = Foo { field$0 };
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*field*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let field: i32
|
||||||
|
```
|
||||||
|
---
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
field: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo { field: 123 };
|
||||||
|
let Foo { field$0 } = v;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*field*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let field: i32
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
size = 4, align = 4
|
||||||
|
---
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
field: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo { field: 123 };
|
||||||
|
let Foo { field$0: _ } = v;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*field*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
field: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T> { field: T }
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo { field: 123 };
|
||||||
|
let _ = (&v).$0field;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*field*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
field: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
check(
|
||||||
|
r#"
|
||||||
|
struct Foo<T>(T);
|
||||||
|
|
||||||
|
fn bar() {
|
||||||
|
let v = Foo(123);
|
||||||
|
let _ = v.$00;
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
*0*
|
||||||
|
|
||||||
|
```rust
|
||||||
|
ra_test_fixture::Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
```rust
|
||||||
|
0: T
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
`T` = `i32`
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -86,7 +86,7 @@ pub use crate::{
|
||||||
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
highlight_related::{HighlightRelatedConfig, HighlightedRange},
|
||||||
hover::{
|
hover::{
|
||||||
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
|
HoverAction, HoverConfig, HoverDocFormat, HoverGotoTypeData, HoverResult,
|
||||||
MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind,
|
MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, SubstTyLen,
|
||||||
},
|
},
|
||||||
inlay_hints::{
|
inlay_hints::{
|
||||||
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
|
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
|
||||||
|
|
|
@ -112,7 +112,7 @@ pub(crate) fn find_all_refs(
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
let def = match NameClass::classify(sema, &name)? {
|
let def = match NameClass::classify(sema, &name)? {
|
||||||
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
NameClass::PatFieldShorthand { local_def: _, field_ref } => {
|
NameClass::PatFieldShorthand { local_def: _, field_ref, adt_subst: _ } => {
|
||||||
Definition::Field(field_ref)
|
Definition::Field(field_ref)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -156,10 +156,12 @@ pub(crate) fn find_defs<'a>(
|
||||||
let def = match name_like {
|
let def = match name_like {
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
match NameRefClass::classify(sema, &name_ref)? {
|
match NameRefClass::classify(sema, &name_ref)? {
|
||||||
NameRefClass::Definition(def) => def,
|
NameRefClass::Definition(def, _) => def,
|
||||||
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
NameRefClass::FieldShorthand {
|
||||||
Definition::Local(local_ref)
|
local_ref,
|
||||||
}
|
field_ref: _,
|
||||||
|
adt_subst: _,
|
||||||
|
} => Definition::Local(local_ref),
|
||||||
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
||||||
Definition::ExternCrateDecl(decl)
|
Definition::ExternCrateDecl(decl)
|
||||||
}
|
}
|
||||||
|
@ -167,14 +169,14 @@ pub(crate) fn find_defs<'a>(
|
||||||
}
|
}
|
||||||
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
|
ast::NameLike::Name(name) => match NameClass::classify(sema, &name)? {
|
||||||
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
|
||||||
Definition::Local(local_def)
|
Definition::Local(local_def)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
ast::NameLike::Lifetime(lifetime) => {
|
ast::NameLike::Lifetime(lifetime) => {
|
||||||
NameRefClass::classify_lifetime(sema, &lifetime)
|
NameRefClass::classify_lifetime(sema, &lifetime)
|
||||||
.and_then(|class| match class {
|
.and_then(|class| match class {
|
||||||
NameRefClass::Definition(it) => Some(it),
|
NameRefClass::Definition(it, _) => Some(it),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
|
|
@ -242,7 +242,7 @@ fn find_definitions(
|
||||||
ast::NameLike::Name(name) => NameClass::classify(sema, name)
|
ast::NameLike::Name(name) => NameClass::classify(sema, name)
|
||||||
.map(|class| match class {
|
.map(|class| match class {
|
||||||
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
NameClass::Definition(it) | NameClass::ConstReference(it) => it,
|
||||||
NameClass::PatFieldShorthand { local_def, field_ref: _ } => {
|
NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
|
||||||
Definition::Local(local_def)
|
Definition::Local(local_def)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -250,8 +250,8 @@ fn find_definitions(
|
||||||
ast::NameLike::NameRef(name_ref) => {
|
ast::NameLike::NameRef(name_ref) => {
|
||||||
NameRefClass::classify(sema, name_ref)
|
NameRefClass::classify(sema, name_ref)
|
||||||
.map(|class| match class {
|
.map(|class| match class {
|
||||||
NameRefClass::Definition(def) => def,
|
NameRefClass::Definition(def, _) => def,
|
||||||
NameRefClass::FieldShorthand { local_ref, field_ref: _ } => {
|
NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ } => {
|
||||||
Definition::Local(local_ref)
|
Definition::Local(local_ref)
|
||||||
}
|
}
|
||||||
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
NameRefClass::ExternCrateShorthand { decl, .. } => {
|
||||||
|
@ -276,7 +276,7 @@ fn find_definitions(
|
||||||
ast::NameLike::Lifetime(lifetime) => {
|
ast::NameLike::Lifetime(lifetime) => {
|
||||||
NameRefClass::classify_lifetime(sema, lifetime)
|
NameRefClass::classify_lifetime(sema, lifetime)
|
||||||
.and_then(|class| match class {
|
.and_then(|class| match class {
|
||||||
NameRefClass::Definition(def) => Some(def),
|
NameRefClass::Definition(def, _) => Some(def),
|
||||||
_ => None,
|
_ => None,
|
||||||
})
|
})
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
|
|
|
@ -13,11 +13,10 @@ use ide_db::{
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
|
use syntax::{AstNode, SyntaxKind::*, SyntaxNode, TextRange, T};
|
||||||
|
|
||||||
use crate::inlay_hints::InlayFieldsToResolve;
|
|
||||||
use crate::navigation_target::UpmappingResult;
|
use crate::navigation_target::UpmappingResult;
|
||||||
use crate::{
|
use crate::{
|
||||||
hover::hover_for_definition,
|
hover::{hover_for_definition, SubstTyLen},
|
||||||
inlay_hints::AdjustmentHintsMode,
|
inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve},
|
||||||
moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind},
|
moniker::{def_to_kind, def_to_moniker, MonikerResult, SymbolInformationKind},
|
||||||
parent_module::crates_for,
|
parent_module::crates_for,
|
||||||
Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
|
Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav,
|
||||||
|
@ -186,6 +185,7 @@ impl StaticIndex<'_> {
|
||||||
max_trait_assoc_items_count: None,
|
max_trait_assoc_items_count: None,
|
||||||
max_fields_count: Some(5),
|
max_fields_count: Some(5),
|
||||||
max_enum_variants_count: Some(5),
|
max_enum_variants_count: Some(5),
|
||||||
|
max_subst_ty_len: SubstTyLen::Unlimited,
|
||||||
};
|
};
|
||||||
let tokens = tokens.filter(|token| {
|
let tokens = tokens.filter(|token| {
|
||||||
matches!(
|
matches!(
|
||||||
|
@ -210,6 +210,7 @@ impl StaticIndex<'_> {
|
||||||
&sema,
|
&sema,
|
||||||
file_id,
|
file_id,
|
||||||
def,
|
def,
|
||||||
|
None,
|
||||||
&node,
|
&node,
|
||||||
None,
|
None,
|
||||||
false,
|
false,
|
||||||
|
|
|
@ -76,7 +76,7 @@ pub(super) fn name_like(
|
||||||
Some(IdentClass::NameClass(NameClass::Definition(def))) => {
|
Some(IdentClass::NameClass(NameClass::Definition(def))) => {
|
||||||
highlight_def(sema, krate, def) | HlMod::Definition
|
highlight_def(sema, krate, def) | HlMod::Definition
|
||||||
}
|
}
|
||||||
Some(IdentClass::NameRefClass(NameRefClass::Definition(def))) => {
|
Some(IdentClass::NameRefClass(NameRefClass::Definition(def, _))) => {
|
||||||
highlight_def(sema, krate, def)
|
highlight_def(sema, krate, def)
|
||||||
}
|
}
|
||||||
// FIXME: Fallback for 'static and '_, as we do not resolve these yet
|
// FIXME: Fallback for 'static and '_, as we do not resolve these yet
|
||||||
|
@ -260,7 +260,7 @@ fn highlight_name_ref(
|
||||||
None => return HlTag::UnresolvedReference.into(),
|
None => return HlTag::UnresolvedReference.into(),
|
||||||
};
|
};
|
||||||
let mut h = match name_class {
|
let mut h = match name_class {
|
||||||
NameRefClass::Definition(def) => {
|
NameRefClass::Definition(def, _) => {
|
||||||
if let Definition::Local(local) = &def {
|
if let Definition::Local(local) = &def {
|
||||||
let name = local.name(db);
|
let name = local.name(db);
|
||||||
let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
|
let shadow_count = bindings_shadow_count.entry(name.clone()).or_default();
|
||||||
|
|
|
@ -50,6 +50,14 @@ mod patch_old_style;
|
||||||
// - Don't use abbreviations unless really necessary
|
// - Don't use abbreviations unless really necessary
|
||||||
// - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
|
// - foo_command = overrides the subcommand, foo_overrideCommand allows full overwriting, extra args only applies for foo_command
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum MaxSubstitutionLength {
|
||||||
|
Hide,
|
||||||
|
#[serde(untagged)]
|
||||||
|
Limit(usize),
|
||||||
|
}
|
||||||
|
|
||||||
// Defines the server-side configuration of the rust-analyzer. We generate
|
// Defines the server-side configuration of the rust-analyzer. We generate
|
||||||
// *parts* of VS Code's `package.json` config from this. Run `cargo test` to
|
// *parts* of VS Code's `package.json` config from this. Run `cargo test` to
|
||||||
// re-generate that file.
|
// re-generate that file.
|
||||||
|
@ -119,6 +127,12 @@ config_data! {
|
||||||
hover_documentation_keywords_enable: bool = true,
|
hover_documentation_keywords_enable: bool = true,
|
||||||
/// Use markdown syntax for links on hover.
|
/// Use markdown syntax for links on hover.
|
||||||
hover_links_enable: bool = true,
|
hover_links_enable: bool = true,
|
||||||
|
/// Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
|
||||||
|
///
|
||||||
|
/// This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
|
||||||
|
///
|
||||||
|
/// The default is 20 characters.
|
||||||
|
hover_maxSubstitutionLength: Option<MaxSubstitutionLength> = Some(MaxSubstitutionLength::Limit(20)),
|
||||||
/// How to render the align information in a memory layout hover.
|
/// How to render the align information in a memory layout hover.
|
||||||
hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
|
hover_memoryLayout_alignment: Option<MemoryLayoutHoverRenderKindDef> = Some(MemoryLayoutHoverRenderKindDef::Hexadecimal),
|
||||||
/// Whether to show memory layout data on hover.
|
/// Whether to show memory layout data on hover.
|
||||||
|
@ -1532,6 +1546,11 @@ impl Config {
|
||||||
max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
|
max_trait_assoc_items_count: self.hover_show_traitAssocItems().to_owned(),
|
||||||
max_fields_count: self.hover_show_fields().to_owned(),
|
max_fields_count: self.hover_show_fields().to_owned(),
|
||||||
max_enum_variants_count: self.hover_show_enumVariants().to_owned(),
|
max_enum_variants_count: self.hover_show_enumVariants().to_owned(),
|
||||||
|
max_subst_ty_len: match self.hover_maxSubstitutionLength() {
|
||||||
|
Some(MaxSubstitutionLength::Hide) => ide::SubstTyLen::Hide,
|
||||||
|
Some(MaxSubstitutionLength::Limit(limit)) => ide::SubstTyLen::LimitTo(*limit),
|
||||||
|
None => ide::SubstTyLen::Unlimited,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3433,6 +3452,20 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
|
||||||
"Use `cargo metadata` to query sysroot metadata."
|
"Use `cargo metadata` to query sysroot metadata."
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
"Option<MaxSubstitutionLength>" => set! {
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["hide"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
_ => panic!("missing entry for {ty}: {default} (field {field})"),
|
_ => panic!("missing entry for {ty}: {default} (field {field})"),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -512,6 +512,15 @@ Whether to show keyword hover popups. Only applies when
|
||||||
--
|
--
|
||||||
Use markdown syntax for links on hover.
|
Use markdown syntax for links on hover.
|
||||||
--
|
--
|
||||||
|
[[rust-analyzer.hover.maxSubstitutionLength]]rust-analyzer.hover.maxSubstitutionLength (default: `20`)::
|
||||||
|
+
|
||||||
|
--
|
||||||
|
Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.
|
||||||
|
|
||||||
|
This can take three values: `null` means "unlimited", the string `"hide"` means to not show generic substitutions at all, and a number means to limit them to X characters.
|
||||||
|
|
||||||
|
The default is 20 characters.
|
||||||
|
--
|
||||||
[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`)::
|
[[rust-analyzer.hover.memoryLayout.alignment]]rust-analyzer.hover.memoryLayout.alignment (default: `"hexadecimal"`)::
|
||||||
+
|
+
|
||||||
--
|
--
|
||||||
|
|
|
@ -1530,6 +1530,29 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"title": "hover",
|
||||||
|
"properties": {
|
||||||
|
"rust-analyzer.hover.maxSubstitutionLength": {
|
||||||
|
"markdownDescription": "Whether to show what types are used as generic arguments in calls etc. on hover, and what is their max length to show such types, beyond it they will be shown with ellipsis.\n\nThis can take three values: `null` means \"unlimited\", the string `\"hide\"` means to not show generic substitutions at all, and a number means to limit them to X characters.\n\nThe default is 20 characters.",
|
||||||
|
"default": 20,
|
||||||
|
"anyOf": [
|
||||||
|
{
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"hide"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"title": "hover",
|
"title": "hover",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
Loading…
Reference in a new issue