mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-26 21:13:37 +00:00
feat: Add binding mode inlay hints
This commit is contained in:
parent
06448c5548
commit
6b696fced8
12 changed files with 201 additions and 41 deletions
|
@ -105,7 +105,7 @@ impl<'a> PatCtxt<'a> {
|
|||
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
|
||||
unadjusted_pat,
|
||||
|subpattern, ref_ty| Pat {
|
||||
ty: ref_ty.target.clone(),
|
||||
ty: ref_ty.clone(),
|
||||
kind: Box::new(PatKind::Deref { subpattern }),
|
||||
},
|
||||
)
|
||||
|
|
|
@ -297,7 +297,7 @@ pub struct InferenceResult {
|
|||
/// Interned Unknown to return references to.
|
||||
standard_types: InternedStandardTypes,
|
||||
/// Stores the types which were implicitly dereferenced in pattern binding modes.
|
||||
pub pat_adjustments: FxHashMap<PatId, Vec<Adjustment>>,
|
||||
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
|
||||
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
|
||||
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ impl<'a> InferenceContext<'a> {
|
|||
adjustment.target = table.resolve_completely(adjustment.target.clone());
|
||||
}
|
||||
for adjustment in result.pat_adjustments.values_mut().flatten() {
|
||||
adjustment.target = table.resolve_completely(adjustment.target.clone());
|
||||
*adjustment = table.resolve_completely(adjustment.clone());
|
||||
}
|
||||
result
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ use hir_def::{
|
|||
use hir_expand::name::Name;
|
||||
|
||||
use crate::{
|
||||
infer::{
|
||||
Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
|
||||
},
|
||||
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
|
||||
lower::lower_to_chalk_mutability,
|
||||
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
|
||||
TyKind,
|
||||
|
@ -105,10 +103,7 @@ impl<'a> InferenceContext<'a> {
|
|||
if is_non_ref_pat(&self.body, pat) {
|
||||
let mut pat_adjustments = Vec::new();
|
||||
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
|
||||
pat_adjustments.push(Adjustment {
|
||||
target: expected.clone(),
|
||||
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
|
||||
});
|
||||
pat_adjustments.push(expected.clone());
|
||||
expected = self.resolve_ty_shallow(inner);
|
||||
default_bm = match default_bm {
|
||||
BindingMode::Move => BindingMode::Ref(mutability),
|
||||
|
|
|
@ -47,7 +47,8 @@ pub use autoderef::autoderef;
|
|||
pub use builder::{ParamKind, TyBuilder};
|
||||
pub use chalk_ext::*;
|
||||
pub use infer::{
|
||||
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
|
||||
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
|
||||
InferenceResult,
|
||||
};
|
||||
pub use interner::Interner;
|
||||
pub use lower::{
|
||||
|
|
|
@ -3332,6 +3332,12 @@ impl Callable {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||
pub enum BindingMode {
|
||||
Move,
|
||||
Ref(Mutability),
|
||||
}
|
||||
|
||||
/// For IDE only
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub enum ScopeDef {
|
||||
|
|
|
@ -30,9 +30,9 @@ use crate::{
|
|||
db::HirDatabase,
|
||||
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
|
||||
source_analyzer::{resolve_hir_path, SourceAnalyzer},
|
||||
Access, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, HirFileId, Impl,
|
||||
InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, ScopeDef,
|
||||
ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
|
||||
HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path,
|
||||
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
@ -336,6 +336,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
|||
self.imp.type_of_self(param)
|
||||
}
|
||||
|
||||
pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
|
||||
self.imp.pattern_adjustments(pat)
|
||||
}
|
||||
|
||||
pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
|
||||
self.imp.binding_mode_of_pat(pat)
|
||||
}
|
||||
|
||||
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
|
||||
self.imp.resolve_method_call(call).map(Function::from)
|
||||
}
|
||||
|
@ -951,6 +959,16 @@ impl<'db> SemanticsImpl<'db> {
|
|||
self.analyze(param.syntax())?.type_of_self(self.db, param)
|
||||
}
|
||||
|
||||
fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
|
||||
self.analyze(pat.syntax())
|
||||
.and_then(|it| it.pattern_adjustments(self.db, pat))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
|
||||
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
|
||||
}
|
||||
|
||||
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
|
||||
self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
|
||||
}
|
||||
|
|
|
@ -34,15 +34,16 @@ use hir_ty::{
|
|||
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
|
||||
TyLoweringContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BuiltinAttr, BuiltinType, Const,
|
||||
Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, Type, TypeAlias,
|
||||
Variant,
|
||||
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
|
||||
BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule,
|
||||
Trait, Type, TypeAlias, Variant,
|
||||
};
|
||||
|
||||
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
|
||||
|
@ -182,7 +183,7 @@ impl SourceAnalyzer {
|
|||
let coerced = infer
|
||||
.pat_adjustments
|
||||
.get(&pat_id)
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
|
||||
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.clone()));
|
||||
let ty = infer[pat_id].clone();
|
||||
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
|
||||
Some((mk_ty(ty), coerced.map(mk_ty)))
|
||||
|
@ -199,6 +200,38 @@ impl SourceAnalyzer {
|
|||
Some(Type::new_with_resolver(db, &self.resolver, ty))
|
||||
}
|
||||
|
||||
pub(crate) fn binding_mode_of_pat(
|
||||
&self,
|
||||
_db: &dyn HirDatabase,
|
||||
pat: &ast::IdentPat,
|
||||
) -> Option<BindingMode> {
|
||||
let pat_id = self.pat_id(&pat.clone().into())?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
|
||||
hir_ty::BindingMode::Move => BindingMode::Move,
|
||||
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
|
||||
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
|
||||
BindingMode::Ref(Mutability::Shared)
|
||||
}
|
||||
})
|
||||
}
|
||||
pub(crate) fn pattern_adjustments(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
pat: &ast::Pat,
|
||||
) -> Option<SmallVec<[Type; 1]>> {
|
||||
let pat_id = self.pat_id(&pat)?;
|
||||
let infer = self.infer.as_ref()?;
|
||||
Some(
|
||||
infer
|
||||
.pat_adjustments
|
||||
.get(&pat_id)?
|
||||
.iter()
|
||||
.map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone()))
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn resolve_method_call(
|
||||
&self,
|
||||
db: &dyn HirDatabase,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use either::Either;
|
||||
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
|
||||
use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
|
||||
use ide_db::{
|
||||
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
|
||||
RootDatabase,
|
||||
|
@ -21,6 +21,7 @@ pub struct InlayHintsConfig {
|
|||
pub chaining_hints: bool,
|
||||
pub reborrow_hints: ReborrowHints,
|
||||
pub closure_return_type_hints: bool,
|
||||
pub binding_mode_hints: bool,
|
||||
pub lifetime_elision_hints: LifetimeElisionHints,
|
||||
pub param_names_for_lifetime_elision_hints: bool,
|
||||
pub hide_named_constructor_hints: bool,
|
||||
|
@ -43,10 +44,11 @@ pub enum ReborrowHints {
|
|||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub enum InlayKind {
|
||||
BindingModeHint,
|
||||
ChainingHint,
|
||||
ClosureReturnTypeHint,
|
||||
GenericParamListHint,
|
||||
ImplicitReborrow,
|
||||
ImplicitReborrowHint,
|
||||
LifetimeHint,
|
||||
ParameterHint,
|
||||
TypeHint,
|
||||
|
@ -135,8 +137,11 @@ fn hints(
|
|||
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
|
||||
_ => None,
|
||||
};
|
||||
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
|
||||
bind_pat_hints(hints, sema, config, &it);
|
||||
} else if let Some(it) = ast::Pat::cast(node.clone()) {
|
||||
binding_mode_hints(hints, sema, config, &it);
|
||||
if let ast::Pat::IdentPat(it) = it {
|
||||
bind_pat_hints(hints, sema, config, &it);
|
||||
}
|
||||
} else if let Some(it) = ast::Fn::cast(node) {
|
||||
lifetime_hints(hints, config, it);
|
||||
}
|
||||
|
@ -383,6 +388,8 @@ fn reborrow_hints(
|
|||
return None;
|
||||
}
|
||||
|
||||
// let descended = sema.descend_node_into_attributes(expr.clone()).pop();
|
||||
// let desc_expr = descended.as_ref().unwrap_or(expr);
|
||||
let mutability = sema.is_implicit_reborrow(expr)?;
|
||||
let label = match mutability {
|
||||
hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
|
||||
|
@ -391,7 +398,7 @@ fn reborrow_hints(
|
|||
};
|
||||
acc.push(InlayHint {
|
||||
range: expr.syntax().text_range(),
|
||||
kind: InlayKind::ImplicitReborrow,
|
||||
kind: InlayKind::ImplicitReborrowHint,
|
||||
label: SmolStr::new_inline(label),
|
||||
});
|
||||
Some(())
|
||||
|
@ -497,6 +504,51 @@ fn param_name_hints(
|
|||
Some(())
|
||||
}
|
||||
|
||||
fn binding_mode_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
config: &InlayHintsConfig,
|
||||
pat: &ast::Pat,
|
||||
) -> Option<()> {
|
||||
if !config.binding_mode_hints {
|
||||
return None;
|
||||
}
|
||||
|
||||
let range = pat.syntax().text_range();
|
||||
sema.pattern_adjustments(&pat).iter().for_each(|ty| {
|
||||
let reference = ty.is_reference();
|
||||
let mut_reference = ty.is_mutable_reference();
|
||||
let r = match (reference, mut_reference) {
|
||||
(true, true) => "&mut",
|
||||
(true, false) => "&",
|
||||
_ => return,
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
range,
|
||||
kind: InlayKind::BindingModeHint,
|
||||
label: SmolStr::new_inline(r),
|
||||
});
|
||||
});
|
||||
match pat {
|
||||
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
|
||||
let bm = sema.binding_mode_of_pat(pat)?;
|
||||
let bm = match bm {
|
||||
hir::BindingMode::Move => return None,
|
||||
hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
|
||||
hir::BindingMode::Ref(Mutability::Shared) => "ref",
|
||||
};
|
||||
acc.push(InlayHint {
|
||||
range,
|
||||
kind: InlayKind::BindingModeHint,
|
||||
label: SmolStr::new_inline(bm),
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
Some(())
|
||||
}
|
||||
|
||||
fn bind_pat_hints(
|
||||
acc: &mut Vec<InlayHint>,
|
||||
sema: &Semantics<RootDatabase>,
|
||||
|
@ -681,6 +733,7 @@ fn should_not_display_type_hint(
|
|||
match_ast! {
|
||||
match node {
|
||||
ast::LetStmt(it) => return it.ty().is_some(),
|
||||
// FIXME: We might wanna show type hints in parameters for non-top level patterns as well
|
||||
ast::Param(it) => return it.ty().is_some(),
|
||||
ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
|
||||
ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
|
||||
|
@ -866,9 +919,10 @@ mod tests {
|
|||
parameter_hints: false,
|
||||
chaining_hints: false,
|
||||
lifetime_elision_hints: LifetimeElisionHints::Never,
|
||||
hide_named_constructor_hints: false,
|
||||
closure_return_type_hints: false,
|
||||
reborrow_hints: ReborrowHints::Always,
|
||||
binding_mode_hints: false,
|
||||
hide_named_constructor_hints: false,
|
||||
param_names_for_lifetime_elision_hints: false,
|
||||
max_length: None,
|
||||
};
|
||||
|
@ -878,6 +932,7 @@ mod tests {
|
|||
chaining_hints: true,
|
||||
reborrow_hints: ReborrowHints::Always,
|
||||
closure_return_type_hints: true,
|
||||
binding_mode_hints: true,
|
||||
lifetime_elision_hints: LifetimeElisionHints::Always,
|
||||
..DISABLED_CONFIG
|
||||
};
|
||||
|
@ -2191,6 +2246,51 @@ fn ref_mut_id(mut_ref: &mut ()) -> &mut () {
|
|||
fn ref_id(shared_ref: &()) -> &() {
|
||||
shared_ref
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hints_binding_modes() {
|
||||
check_with_config(
|
||||
InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
|
||||
r#"
|
||||
fn __(
|
||||
(x,): (u32,),
|
||||
(x,): &(u32,),
|
||||
//^^^^&
|
||||
//^ ref
|
||||
(x,): &mut (u32,)
|
||||
//^^^^&mut
|
||||
//^ ref mut
|
||||
) {
|
||||
let (x,) = (0,);
|
||||
let (x,) = &(0,);
|
||||
//^^^^ &
|
||||
//^ ref
|
||||
let (x,) = &mut (0,);
|
||||
//^^^^ &mut
|
||||
//^ ref mut
|
||||
let &mut (x,) = &mut (0,);
|
||||
let (ref mut x,) = &mut (0,);
|
||||
//^^^^^^^^^^^^ &mut
|
||||
let &mut (ref mut x,) = &mut (0,);
|
||||
let (mut x,) = &mut (0,);
|
||||
//^^^^^^^^ &mut
|
||||
match (0,) {
|
||||
(x,) => ()
|
||||
}
|
||||
match &(0,) {
|
||||
(x,) => ()
|
||||
//^^^^ &
|
||||
//^ ref
|
||||
}
|
||||
match &mut (0,) {
|
||||
(x,) => ()
|
||||
//^^^^ &mut
|
||||
//^ ref mut
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -114,6 +114,7 @@ impl StaticIndex<'_> {
|
|||
reborrow_hints: crate::ReborrowHints::Never,
|
||||
hide_named_constructor_hints: false,
|
||||
param_names_for_lifetime_elision_hints: false,
|
||||
binding_mode_hints: false,
|
||||
max_length: Some(25),
|
||||
},
|
||||
file_id,
|
||||
|
|
|
@ -254,6 +254,8 @@ config_data! {
|
|||
/// The path structure for newly inserted paths to use.
|
||||
imports_prefix: ImportPrefixDef = "\"plain\"",
|
||||
|
||||
/// Whether to show inlay type hints for binding modes.
|
||||
inlayHints_bindingModeHints_enable: bool = "false",
|
||||
/// Whether to show inlay type hints for method chains.
|
||||
inlayHints_chainingHints_enable: bool = "true",
|
||||
/// Whether to show inlay type hints for return types of closures with blocks.
|
||||
|
@ -997,6 +999,7 @@ impl Config {
|
|||
ReborrowHintsDef::Never => ide::ReborrowHints::Never,
|
||||
ReborrowHintsDef::Mutable => ide::ReborrowHints::MutableOnly,
|
||||
},
|
||||
binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
|
||||
param_names_for_lifetime_elision_hints: self
|
||||
.data
|
||||
.inlayHints_lifetimeElisionHints_useParameterNames,
|
||||
|
|
|
@ -418,9 +418,9 @@ pub(crate) fn inlay_hint(
|
|||
lsp_types::InlayHint {
|
||||
position: match inlay_hint.kind {
|
||||
// before annotated thing
|
||||
InlayKind::ParameterHint | InlayKind::ImplicitReborrow => {
|
||||
position(line_index, inlay_hint.range.start())
|
||||
}
|
||||
InlayKind::ParameterHint
|
||||
| InlayKind::ImplicitReborrowHint
|
||||
| InlayKind::BindingModeHint => position(line_index, inlay_hint.range.start()),
|
||||
// after annotated thing
|
||||
InlayKind::ClosureReturnTypeHint
|
||||
| InlayKind::TypeHint
|
||||
|
@ -439,27 +439,30 @@ pub(crate) fn inlay_hint(
|
|||
InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
|
||||
Some(lsp_types::InlayHintKind::TYPE)
|
||||
}
|
||||
InlayKind::GenericParamListHint
|
||||
InlayKind::BindingModeHint
|
||||
| InlayKind::GenericParamListHint
|
||||
| InlayKind::LifetimeHint
|
||||
| InlayKind::ImplicitReborrow => None,
|
||||
| InlayKind::ImplicitReborrowHint => None,
|
||||
},
|
||||
tooltip: None,
|
||||
padding_left: Some(match inlay_hint.kind {
|
||||
InlayKind::TypeHint => !render_colons,
|
||||
InlayKind::ParameterHint | InlayKind::ClosureReturnTypeHint => false,
|
||||
InlayKind::ChainingHint => true,
|
||||
InlayKind::GenericParamListHint => false,
|
||||
InlayKind::LifetimeHint => false,
|
||||
InlayKind::ImplicitReborrow => false,
|
||||
InlayKind::BindingModeHint
|
||||
| InlayKind::ClosureReturnTypeHint
|
||||
| InlayKind::GenericParamListHint
|
||||
| InlayKind::ImplicitReborrowHint
|
||||
| InlayKind::LifetimeHint
|
||||
| InlayKind::ParameterHint => false,
|
||||
}),
|
||||
padding_right: Some(match inlay_hint.kind {
|
||||
InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
|
||||
false
|
||||
}
|
||||
InlayKind::ParameterHint => true,
|
||||
InlayKind::LifetimeHint => true,
|
||||
InlayKind::GenericParamListHint => false,
|
||||
InlayKind::ImplicitReborrow => false,
|
||||
InlayKind::ChainingHint
|
||||
| InlayKind::ClosureReturnTypeHint
|
||||
| InlayKind::GenericParamListHint
|
||||
| InlayKind::ImplicitReborrowHint
|
||||
| InlayKind::TypeHint => false,
|
||||
InlayKind::BindingModeHint => inlay_hint.label != "&",
|
||||
InlayKind::ParameterHint | InlayKind::LifetimeHint => true,
|
||||
}),
|
||||
text_edits: None,
|
||||
data: None,
|
||||
|
|
|
@ -273,7 +273,7 @@ impl ast::ArrayExpr {
|
|||
}
|
||||
|
||||
fn is_repeat(&self) -> bool {
|
||||
self.syntax().children_with_tokens().any(|it| it.kind() == T![;])
|
||||
self.semicolon_token().is_some()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue