Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 1dc25e5

Browse files
committed
Auto merge of rust-lang#12253 - Veykril:bm, r=Veykril
feat: Add binding mode inlay hints ![image](https://user-images.githubusercontent.com/3757771/168427387-2f299438-a0cc-496b-a9a5-d689ef6a2b55.png)
2 parents ac4ce42 + 977f0ba commit 1dc25e5

File tree

14 files changed

+212
-42
lines changed

14 files changed

+212
-42
lines changed

crates/hir-ty/src/diagnostics/match_check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ impl<'a> PatCtxt<'a> {
105105
self.infer.pat_adjustments.get(&pat).map(|it| &**it).unwrap_or_default().iter().rev().fold(
106106
unadjusted_pat,
107107
|subpattern, ref_ty| Pat {
108-
ty: ref_ty.target.clone(),
108+
ty: ref_ty.clone(),
109109
kind: Box::new(PatKind::Deref { subpattern }),
110110
},
111111
)

crates/hir-ty/src/infer.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ pub struct InferenceResult {
297297
/// Interned Unknown to return references to.
298298
standard_types: InternedStandardTypes,
299299
/// Stores the types which were implicitly dereferenced in pattern binding modes.
300-
pub pat_adjustments: FxHashMap<PatId, Vec<Adjustment>>,
300+
pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
301301
pub pat_binding_modes: FxHashMap<PatId, BindingMode>,
302302
pub expr_adjustments: FxHashMap<ExprId, Vec<Adjustment>>,
303303
}
@@ -445,7 +445,7 @@ impl<'a> InferenceContext<'a> {
445445
adjustment.target = table.resolve_completely(adjustment.target.clone());
446446
}
447447
for adjustment in result.pat_adjustments.values_mut().flatten() {
448-
adjustment.target = table.resolve_completely(adjustment.target.clone());
448+
*adjustment = table.resolve_completely(adjustment.clone());
449449
}
450450
result
451451
}

crates/hir-ty/src/infer/pat.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,7 @@ use hir_def::{
1111
use hir_expand::name::Name;
1212

1313
use crate::{
14-
infer::{
15-
Adjust, Adjustment, AutoBorrow, BindingMode, Expectation, InferenceContext, TypeMismatch,
16-
},
14+
infer::{BindingMode, Expectation, InferenceContext, TypeMismatch},
1715
lower::lower_to_chalk_mutability,
1816
static_lifetime, ConcreteConst, ConstValue, Interner, Substitution, Ty, TyBuilder, TyExt,
1917
TyKind,
@@ -105,10 +103,7 @@ impl<'a> InferenceContext<'a> {
105103
if is_non_ref_pat(&self.body, pat) {
106104
let mut pat_adjustments = Vec::new();
107105
while let Some((inner, _lifetime, mutability)) = expected.as_reference() {
108-
pat_adjustments.push(Adjustment {
109-
target: expected.clone(),
110-
kind: Adjust::Borrow(AutoBorrow::Ref(mutability)),
111-
});
106+
pat_adjustments.push(expected.clone());
112107
expected = self.resolve_ty_shallow(inner);
113108
default_bm = match default_bm {
114109
BindingMode::Move => BindingMode::Ref(mutability),

crates/hir-ty/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ pub use autoderef::autoderef;
4747
pub use builder::{ParamKind, TyBuilder};
4848
pub use chalk_ext::*;
4949
pub use infer::{
50-
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
50+
could_coerce, could_unify, Adjust, Adjustment, AutoBorrow, BindingMode, InferenceDiagnostic,
51+
InferenceResult,
5152
};
5253
pub use interner::Interner;
5354
pub use lower::{

crates/hir/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3332,6 +3332,12 @@ impl Callable {
33323332
}
33333333
}
33343334

3335+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
3336+
pub enum BindingMode {
3337+
Move,
3338+
Ref(Mutability),
3339+
}
3340+
33353341
/// For IDE only
33363342
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
33373343
pub enum ScopeDef {

crates/hir/src/semantics.rs

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ use crate::{
3030
db::HirDatabase,
3131
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
3232
source_analyzer::{resolve_hir_path, SourceAnalyzer},
33-
Access, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource, HirFileId, Impl,
34-
InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path, ScopeDef,
35-
ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
33+
Access, BindingMode, BuiltinAttr, Callable, ConstParam, Crate, Field, Function, HasSource,
34+
HirFileId, Impl, InFile, Label, LifetimeParam, Local, Macro, Module, ModuleDef, Name, Path,
35+
ScopeDef, ToolModule, Trait, Type, TypeAlias, TypeParam, VariantDef,
3636
};
3737

3838
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -336,6 +336,14 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
336336
self.imp.type_of_self(param)
337337
}
338338

339+
pub fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
340+
self.imp.pattern_adjustments(pat)
341+
}
342+
343+
pub fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
344+
self.imp.binding_mode_of_pat(pat)
345+
}
346+
339347
pub fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<Function> {
340348
self.imp.resolve_method_call(call).map(Function::from)
341349
}
@@ -951,6 +959,16 @@ impl<'db> SemanticsImpl<'db> {
951959
self.analyze(param.syntax())?.type_of_self(self.db, param)
952960
}
953961

962+
fn pattern_adjustments(&self, pat: &ast::Pat) -> SmallVec<[Type; 1]> {
963+
self.analyze(pat.syntax())
964+
.and_then(|it| it.pattern_adjustments(self.db, pat))
965+
.unwrap_or_default()
966+
}
967+
968+
fn binding_mode_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingMode> {
969+
self.analyze(pat.syntax())?.binding_mode_of_pat(self.db, pat)
970+
}
971+
954972
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
955973
self.analyze(call.syntax())?.resolve_method_call(self.db, call).map(|(id, _)| id)
956974
}

crates/hir/src/source_analyzer.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,16 @@ use hir_ty::{
3434
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
3535
TyLoweringContext,
3636
};
37+
use smallvec::SmallVec;
3738
use syntax::{
3839
ast::{self, AstNode},
3940
SyntaxKind, SyntaxNode, TextRange, TextSize,
4041
};
4142

4243
use crate::{
43-
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BuiltinAttr, BuiltinType, Const,
44-
Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule, Trait, Type, TypeAlias,
45-
Variant,
44+
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
45+
BuiltinType, Const, Field, Function, Local, Macro, ModuleDef, Static, Struct, ToolModule,
46+
Trait, Type, TypeAlias, Variant,
4647
};
4748

4849
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
@@ -182,7 +183,7 @@ impl SourceAnalyzer {
182183
let coerced = infer
183184
.pat_adjustments
184185
.get(&pat_id)
185-
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
186+
.and_then(|adjusts| adjusts.last().map(|adjust| adjust.clone()));
186187
let ty = infer[pat_id].clone();
187188
let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
188189
Some((mk_ty(ty), coerced.map(mk_ty)))
@@ -199,6 +200,38 @@ impl SourceAnalyzer {
199200
Some(Type::new_with_resolver(db, &self.resolver, ty))
200201
}
201202

203+
pub(crate) fn binding_mode_of_pat(
204+
&self,
205+
_db: &dyn HirDatabase,
206+
pat: &ast::IdentPat,
207+
) -> Option<BindingMode> {
208+
let pat_id = self.pat_id(&pat.clone().into())?;
209+
let infer = self.infer.as_ref()?;
210+
infer.pat_binding_modes.get(&pat_id).map(|bm| match bm {
211+
hir_ty::BindingMode::Move => BindingMode::Move,
212+
hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
213+
hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
214+
BindingMode::Ref(Mutability::Shared)
215+
}
216+
})
217+
}
218+
pub(crate) fn pattern_adjustments(
219+
&self,
220+
db: &dyn HirDatabase,
221+
pat: &ast::Pat,
222+
) -> Option<SmallVec<[Type; 1]>> {
223+
let pat_id = self.pat_id(&pat)?;
224+
let infer = self.infer.as_ref()?;
225+
Some(
226+
infer
227+
.pat_adjustments
228+
.get(&pat_id)?
229+
.iter()
230+
.map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone()))
231+
.collect(),
232+
)
233+
}
234+
202235
pub(crate) fn resolve_method_call(
203236
&self,
204237
db: &dyn HirDatabase,

crates/ide/src/inlay_hints.rs

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use either::Either;
2-
use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
2+
use hir::{known, Callable, HasVisibility, HirDisplay, Mutability, Semantics, TypeInfo};
33
use ide_db::{
44
base_db::FileRange, famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap,
55
RootDatabase,
@@ -21,6 +21,7 @@ pub struct InlayHintsConfig {
2121
pub chaining_hints: bool,
2222
pub reborrow_hints: ReborrowHints,
2323
pub closure_return_type_hints: bool,
24+
pub binding_mode_hints: bool,
2425
pub lifetime_elision_hints: LifetimeElisionHints,
2526
pub param_names_for_lifetime_elision_hints: bool,
2627
pub hide_named_constructor_hints: bool,
@@ -43,10 +44,11 @@ pub enum ReborrowHints {
4344

4445
#[derive(Clone, Debug, PartialEq, Eq)]
4546
pub enum InlayKind {
47+
BindingModeHint,
4648
ChainingHint,
4749
ClosureReturnTypeHint,
4850
GenericParamListHint,
49-
ImplicitReborrow,
51+
ImplicitReborrowHint,
5052
LifetimeHint,
5153
ParameterHint,
5254
TypeHint,
@@ -135,8 +137,11 @@ fn hints(
135137
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
136138
_ => None,
137139
};
138-
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
139-
bind_pat_hints(hints, sema, config, &it);
140+
} else if let Some(it) = ast::Pat::cast(node.clone()) {
141+
binding_mode_hints(hints, sema, config, &it);
142+
if let ast::Pat::IdentPat(it) = it {
143+
bind_pat_hints(hints, sema, config, &it);
144+
}
140145
} else if let Some(it) = ast::Fn::cast(node) {
141146
lifetime_hints(hints, config, it);
142147
}
@@ -383,15 +388,17 @@ fn reborrow_hints(
383388
return None;
384389
}
385390

386-
let mutability = sema.is_implicit_reborrow(expr)?;
391+
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
392+
let desc_expr = descended.as_ref().unwrap_or(expr);
393+
let mutability = sema.is_implicit_reborrow(desc_expr)?;
387394
let label = match mutability {
388395
hir::Mutability::Shared if config.reborrow_hints != ReborrowHints::MutableOnly => "&*",
389396
hir::Mutability::Mut => "&mut *",
390397
_ => return None,
391398
};
392399
acc.push(InlayHint {
393400
range: expr.syntax().text_range(),
394-
kind: InlayKind::ImplicitReborrow,
401+
kind: InlayKind::ImplicitReborrowHint,
395402
label: SmolStr::new_inline(label),
396403
});
397404
Some(())
@@ -497,6 +504,51 @@ fn param_name_hints(
497504
Some(())
498505
}
499506

507+
fn binding_mode_hints(
508+
acc: &mut Vec<InlayHint>,
509+
sema: &Semantics<RootDatabase>,
510+
config: &InlayHintsConfig,
511+
pat: &ast::Pat,
512+
) -> Option<()> {
513+
if !config.binding_mode_hints {
514+
return None;
515+
}
516+
517+
let range = pat.syntax().text_range();
518+
sema.pattern_adjustments(&pat).iter().for_each(|ty| {
519+
let reference = ty.is_reference();
520+
let mut_reference = ty.is_mutable_reference();
521+
let r = match (reference, mut_reference) {
522+
(true, true) => "&mut",
523+
(true, false) => "&",
524+
_ => return,
525+
};
526+
acc.push(InlayHint {
527+
range,
528+
kind: InlayKind::BindingModeHint,
529+
label: SmolStr::new_inline(r),
530+
});
531+
});
532+
match pat {
533+
ast::Pat::IdentPat(pat) if pat.ref_token().is_none() && pat.mut_token().is_none() => {
534+
let bm = sema.binding_mode_of_pat(pat)?;
535+
let bm = match bm {
536+
hir::BindingMode::Move => return None,
537+
hir::BindingMode::Ref(Mutability::Mut) => "ref mut",
538+
hir::BindingMode::Ref(Mutability::Shared) => "ref",
539+
};
540+
acc.push(InlayHint {
541+
range,
542+
kind: InlayKind::BindingModeHint,
543+
label: SmolStr::new_inline(bm),
544+
});
545+
}
546+
_ => (),
547+
}
548+
549+
Some(())
550+
}
551+
500552
fn bind_pat_hints(
501553
acc: &mut Vec<InlayHint>,
502554
sema: &Semantics<RootDatabase>,
@@ -681,6 +733,7 @@ fn should_not_display_type_hint(
681733
match_ast! {
682734
match node {
683735
ast::LetStmt(it) => return it.ty().is_some(),
736+
// FIXME: We might wanna show type hints in parameters for non-top level patterns as well
684737
ast::Param(it) => return it.ty().is_some(),
685738
ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
686739
ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
@@ -866,9 +919,10 @@ mod tests {
866919
parameter_hints: false,
867920
chaining_hints: false,
868921
lifetime_elision_hints: LifetimeElisionHints::Never,
869-
hide_named_constructor_hints: false,
870922
closure_return_type_hints: false,
871923
reborrow_hints: ReborrowHints::Always,
924+
binding_mode_hints: false,
925+
hide_named_constructor_hints: false,
872926
param_names_for_lifetime_elision_hints: false,
873927
max_length: None,
874928
};
@@ -878,6 +932,7 @@ mod tests {
878932
chaining_hints: true,
879933
reborrow_hints: ReborrowHints::Always,
880934
closure_return_type_hints: true,
935+
binding_mode_hints: true,
881936
lifetime_elision_hints: LifetimeElisionHints::Always,
882937
..DISABLED_CONFIG
883938
};
@@ -2191,6 +2246,51 @@ fn ref_mut_id(mut_ref: &mut ()) -> &mut () {
21912246
fn ref_id(shared_ref: &()) -> &() {
21922247
shared_ref
21932248
}
2249+
"#,
2250+
);
2251+
}
2252+
2253+
#[test]
2254+
fn hints_binding_modes() {
2255+
check_with_config(
2256+
InlayHintsConfig { binding_mode_hints: true, ..DISABLED_CONFIG },
2257+
r#"
2258+
fn __(
2259+
(x,): (u32,),
2260+
(x,): &(u32,),
2261+
//^^^^&
2262+
//^ ref
2263+
(x,): &mut (u32,)
2264+
//^^^^&mut
2265+
//^ ref mut
2266+
) {
2267+
let (x,) = (0,);
2268+
let (x,) = &(0,);
2269+
//^^^^ &
2270+
//^ ref
2271+
let (x,) = &mut (0,);
2272+
//^^^^ &mut
2273+
//^ ref mut
2274+
let &mut (x,) = &mut (0,);
2275+
let (ref mut x,) = &mut (0,);
2276+
//^^^^^^^^^^^^ &mut
2277+
let &mut (ref mut x,) = &mut (0,);
2278+
let (mut x,) = &mut (0,);
2279+
//^^^^^^^^ &mut
2280+
match (0,) {
2281+
(x,) => ()
2282+
}
2283+
match &(0,) {
2284+
(x,) => ()
2285+
//^^^^ &
2286+
//^ ref
2287+
}
2288+
match &mut (0,) {
2289+
(x,) => ()
2290+
//^^^^ &mut
2291+
//^ ref mut
2292+
}
2293+
}
21942294
"#,
21952295
);
21962296
}

crates/ide/src/static_index.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl StaticIndex<'_> {
114114
reborrow_hints: crate::ReborrowHints::Never,
115115
hide_named_constructor_hints: false,
116116
param_names_for_lifetime_elision_hints: false,
117+
binding_mode_hints: false,
117118
max_length: Some(25),
118119
},
119120
file_id,

0 commit comments

Comments
 (0)