Skip to content

Commit 966b692

Browse files
bors[bot]Veykril
andauthored
Merge #11771
11771: feat: Visualize compiler inserted reborrows via inlay hints r=Veykril a=Veykril Disabled by default. ![image](https://user-images.githubusercontent.com/3757771/159165178-baaf968a-4381-468e-933f-5326ca1b203d.png) Closes #11275 Co-authored-by: Lukas Wirth <[email protected]>
2 parents a82caff + 5a87f09 commit 966b692

File tree

9 files changed

+127
-17
lines changed

9 files changed

+127
-17
lines changed

crates/hir/src/semantics.rs

+10
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use base_db::{FileId, FileRange};
88
use hir_def::{
99
body, macro_id_to_def_id,
1010
resolver::{self, HasResolver, Resolver, TypeNs},
11+
type_ref::Mutability,
1112
AsMacroCall, FunctionId, MacroId, TraitId, VariantId,
1213
};
1314
use hir_expand::{
@@ -313,6 +314,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
313314
self.imp.resolve_type(ty)
314315
}
315316

317+
// FIXME: Figure out a nice interface to inspect adjustments
318+
pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
319+
self.imp.is_implicit_reborrow(expr)
320+
}
321+
316322
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
317323
self.imp.type_of_expr(expr)
318324
}
@@ -900,6 +906,10 @@ impl<'db> SemanticsImpl<'db> {
900906
Type::new_with_resolver(self.db, &scope.resolver, ty)
901907
}
902908

909+
fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
910+
self.analyze(expr.syntax()).is_implicit_reborrow(self.db, expr)
911+
}
912+
903913
fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
904914
self.analyze(expr.syntax())
905915
.type_of_expr(self.db, expr)

crates/hir/src/source_analyzer.rs

+20-1
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@ use hir_def::{
2020
macro_id_to_def_id,
2121
path::{ModPath, Path, PathKind},
2222
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
23+
type_ref::Mutability,
2324
AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, ModuleDefId, VariantId,
2425
};
2526
use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
2627
use hir_ty::{
2728
diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
28-
InferenceResult, Interner, Substitution, TyExt, TyLoweringContext,
29+
Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
30+
TyLoweringContext,
2931
};
3032
use syntax::{
3133
ast::{self, AstNode},
@@ -139,6 +141,23 @@ impl SourceAnalyzer {
139141
Some(res)
140142
}
141143

144+
pub(crate) fn is_implicit_reborrow(
145+
&self,
146+
db: &dyn HirDatabase,
147+
expr: &ast::Expr,
148+
) -> Option<Mutability> {
149+
let expr_id = self.expr_id(db, expr)?;
150+
let infer = self.infer.as_ref()?;
151+
let adjustments = infer.expr_adjustments.get(&expr_id)?;
152+
adjustments.windows(2).find_map(|slice| match slice {
153+
&[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m {
154+
hir_ty::Mutability::Mut => Mutability::Mut,
155+
hir_ty::Mutability::Not => Mutability::Shared,
156+
}),
157+
_ => None,
158+
})
159+
}
160+
142161
pub(crate) fn type_of_expr(
143162
&self,
144163
db: &dyn HirDatabase,

crates/hir_ty/src/lib.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ use crate::{db::HirDatabase, utils::generics};
5050
pub use autoderef::autoderef;
5151
pub use builder::{ParamKind, TyBuilder};
5252
pub use chalk_ext::*;
53-
pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
53+
pub use infer::{
54+
could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
55+
};
5456
pub use interner::Interner;
5557
pub use lower::{
5658
associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,

crates/ide/src/inlay_hints.rs

+70-13
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub struct InlayHintsConfig {
1919
pub type_hints: bool,
2020
pub parameter_hints: bool,
2121
pub chaining_hints: bool,
22+
pub reborrow_hints: bool,
2223
pub closure_return_type_hints: bool,
2324
pub lifetime_elision_hints: LifetimeElisionHints,
2425
pub param_names_for_lifetime_elision_hints: bool,
@@ -35,6 +36,7 @@ pub enum LifetimeElisionHints {
3536

3637
#[derive(Clone, Debug, PartialEq, Eq)]
3738
pub enum InlayKind {
39+
ImplicitReborrow,
3840
TypeHint,
3941
ParameterHint,
4042
ClosureReturnTypeHint,
@@ -65,10 +67,7 @@ pub struct InlayHint {
6567
//
6668
// * return types of closure expressions with blocks
6769
// * elided lifetimes
68-
//
69-
// **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
70-
// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
71-
// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
70+
// * compiler inserted reborrows
7271
//
7372
// |===
7473
// | Editor | Action Name
@@ -116,17 +115,16 @@ fn hints(
116115
if let Some(expr) = ast::Expr::cast(node.clone()) {
117116
chaining_hints(hints, sema, &famous_defs, config, &expr);
118117
match expr {
119-
ast::Expr::CallExpr(it) => {
120-
param_name_hints(hints, sema, config, ast::Expr::from(it));
121-
}
118+
ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
122119
ast::Expr::MethodCallExpr(it) => {
123-
param_name_hints(hints, sema, config, ast::Expr::from(it));
120+
param_name_hints(hints, sema, config, ast::Expr::from(it))
124121
}
125-
ast::Expr::ClosureExpr(it) => {
126-
closure_ret_hints(hints, sema, &famous_defs, config, it);
127-
}
128-
_ => (),
129-
}
122+
ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it),
123+
// We could show reborrows for all expressions, but usually that is just noise to the user
124+
// and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
125+
ast::Expr::PathExpr(_) => reborrow_hints(hints, sema, config, &expr),
126+
_ => None,
127+
};
130128
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
131129
bind_pat_hints(hints, sema, config, &it);
132130
} else if let Some(it) = ast::Fn::cast(node) {
@@ -365,6 +363,28 @@ fn closure_ret_hints(
365363
Some(())
366364
}
367365

366+
fn reborrow_hints(
367+
acc: &mut Vec<InlayHint>,
368+
sema: &Semantics<RootDatabase>,
369+
config: &InlayHintsConfig,
370+
expr: &ast::Expr,
371+
) -> Option<()> {
372+
if !config.reborrow_hints {
373+
return None;
374+
}
375+
376+
let mutability = sema.is_implicit_reborrow(expr)?;
377+
acc.push(InlayHint {
378+
range: expr.syntax().text_range(),
379+
kind: InlayKind::ImplicitReborrow,
380+
label: match mutability {
381+
hir::Mutability::Shared => SmolStr::new_inline("&*"),
382+
hir::Mutability::Mut => SmolStr::new_inline("&mut *"),
383+
},
384+
});
385+
Some(())
386+
}
387+
368388
fn chaining_hints(
369389
acc: &mut Vec<InlayHint>,
370390
sema: &Semantics<RootDatabase>,
@@ -834,13 +854,15 @@ mod tests {
834854
lifetime_elision_hints: LifetimeElisionHints::Never,
835855
hide_named_constructor_hints: false,
836856
closure_return_type_hints: false,
857+
reborrow_hints: false,
837858
param_names_for_lifetime_elision_hints: false,
838859
max_length: None,
839860
};
840861
const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
841862
type_hints: true,
842863
parameter_hints: true,
843864
chaining_hints: true,
865+
reborrow_hints: true,
844866
closure_return_type_hints: true,
845867
lifetime_elision_hints: LifetimeElisionHints::Always,
846868
..DISABLED_CONFIG
@@ -2115,6 +2137,41 @@ impl () {
21152137
// ^^^<'0, '1>
21162138
// ^'0 ^'1 ^'0
21172139
}
2140+
"#,
2141+
);
2142+
}
2143+
2144+
#[test]
2145+
fn hints_implicit_reborrow() {
2146+
check_with_config(
2147+
InlayHintsConfig { reborrow_hints: true, ..DISABLED_CONFIG },
2148+
r#"
2149+
fn __() {
2150+
let unique = &mut ();
2151+
let r_mov = unique;
2152+
let foo: &mut _ = unique;
2153+
//^^^^^^ &mut *
2154+
ref_mut_id(unique);
2155+
//^^^^^^ &mut *
2156+
let shared = ref_id(unique);
2157+
//^^^^^^ &*
2158+
let mov = shared;
2159+
let r_mov: &_ = shared;
2160+
ref_id(shared);
2161+
2162+
identity(unique);
2163+
identity(shared);
2164+
}
2165+
fn identity<T>(t: T) -> T {
2166+
t
2167+
}
2168+
fn ref_mut_id(x: &mut ()) -> &mut () {
2169+
x
2170+
//^ &mut *
2171+
}
2172+
fn ref_id(x: &()) -> &() {
2173+
x
2174+
}
21182175
"#,
21192176
);
21202177
}

crates/ide/src/static_index.rs

+1
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ impl StaticIndex<'_> {
114114
chaining_hints: true,
115115
closure_return_type_hints: true,
116116
lifetime_elision_hints: LifetimeElisionHints::Never,
117+
reborrow_hints: false,
117118
hide_named_constructor_hints: false,
118119
param_names_for_lifetime_elision_hints: false,
119120
max_length: Some(25),

crates/rust-analyzer/src/config.rs

+3
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ config_data! {
256256
inlayHints_chainingHints: bool = "true",
257257
/// Whether to show inlay type hints for return types of closures with blocks.
258258
inlayHints_closureReturnTypeHints: bool = "false",
259+
/// Whether to show inlay type hints for compiler inserted reborrows.
260+
inlayHints_reborrowHints: bool = "false",
259261
/// Whether to show inlay type hints for elided lifetimes in function signatures.
260262
inlayHints_lifetimeElisionHints: LifetimeElisionDef = "\"never\"",
261263
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -866,6 +868,7 @@ impl Config {
866868
LifetimeElisionDef::SkipTrivial => LifetimeElisionHints::SkipTrivial,
867869
},
868870
hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
871+
reborrow_hints: self.data.inlayHints_reborrowHints,
869872
param_names_for_lifetime_elision_hints: self
870873
.data
871874
.inlayHints_lifetimeElisionHints_useParameterNames,

crates/rust-analyzer/src/to_proto.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -426,7 +426,11 @@ pub(crate) fn inlay_hint(
426426
_ => inlay_hint.label.to_string(),
427427
}),
428428
position: match inlay_hint.kind {
429-
InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
429+
// before annotated thing
430+
InlayKind::ParameterHint | InlayKind::ImplicitReborrow => {
431+
position(line_index, inlay_hint.range.start())
432+
}
433+
// after annotated thing
430434
InlayKind::ClosureReturnTypeHint
431435
| InlayKind::TypeHint
432436
| InlayKind::ChainingHint
@@ -438,7 +442,9 @@ pub(crate) fn inlay_hint(
438442
InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
439443
Some(lsp_ext::InlayHintKind::TYPE)
440444
}
441-
InlayKind::GenericParamListHint | InlayKind::LifetimeHint => None,
445+
InlayKind::GenericParamListHint
446+
| InlayKind::LifetimeHint
447+
| InlayKind::ImplicitReborrow => None,
442448
},
443449
tooltip: None,
444450
padding_left: Some(match inlay_hint.kind {
@@ -447,6 +453,7 @@ pub(crate) fn inlay_hint(
447453
InlayKind::ChainingHint => true,
448454
InlayKind::GenericParamListHint => false,
449455
InlayKind::LifetimeHint => false,
456+
InlayKind::ImplicitReborrow => false,
450457
}),
451458
padding_right: Some(match inlay_hint.kind {
452459
InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
@@ -455,6 +462,7 @@ pub(crate) fn inlay_hint(
455462
InlayKind::ParameterHint => true,
456463
InlayKind::LifetimeHint => true,
457464
InlayKind::GenericParamListHint => false,
465+
InlayKind::ImplicitReborrow => false,
458466
}),
459467
}
460468
}

docs/user/generated_config.adoc

+5
Original file line numberDiff line numberDiff line change
@@ -378,6 +378,11 @@ Whether to show inlay type hints for method chains.
378378
--
379379
Whether to show inlay type hints for return types of closures with blocks.
380380
--
381+
[[rust-analyzer.inlayHints.reborrowHints]]rust-analyzer.inlayHints.reborrowHints (default: `false`)::
382+
+
383+
--
384+
Whether to show inlay type hints for compiler inserted reborrows.
385+
--
381386
[[rust-analyzer.inlayHints.lifetimeElisionHints]]rust-analyzer.inlayHints.lifetimeElisionHints (default: `"never"`)::
382387
+
383388
--

editors/code/package.json

+5
Original file line numberDiff line numberDiff line change
@@ -795,6 +795,11 @@
795795
"default": false,
796796
"type": "boolean"
797797
},
798+
"rust-analyzer.inlayHints.reborrowHints": {
799+
"markdownDescription": "Whether to show inlay type hints for compiler inserted reborrows.",
800+
"default": false,
801+
"type": "boolean"
802+
},
798803
"rust-analyzer.inlayHints.lifetimeElisionHints": {
799804
"markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
800805
"default": "never",

0 commit comments

Comments
 (0)