about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2022-03-20 13:47:16 +0000
committerGitHub <noreply@github.com>2022-03-20 13:47:16 +0000
commit966b6924229c082fee730b6a19ebafaa45b8f91e (patch)
treee8504a056b110cdcd8800198aeba6736b5f7ce03
parenta82caff588f602b171471661f983ee550d4494d6 (diff)
parent5a87f09a714b8299897a72ecfe206460903d703e (diff)
downloadrust-966b6924229c082fee730b6a19ebafaa45b8f91e.tar.gz
rust-966b6924229c082fee730b6a19ebafaa45b8f91e.zip
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 https://github.com/rust-analyzer/rust-analyzer/issues/11275


Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
-rw-r--r--crates/hir/src/semantics.rs10
-rw-r--r--crates/hir/src/source_analyzer.rs21
-rw-r--r--crates/hir_ty/src/lib.rs4
-rw-r--r--crates/ide/src/inlay_hints.rs83
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs3
-rw-r--r--crates/rust-analyzer/src/to_proto.rs12
-rw-r--r--docs/user/generated_config.adoc5
-rw-r--r--editors/code/package.json5
9 files changed, 127 insertions, 17 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 7857edb521a..c2b7e9bb529 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -8,6 +8,7 @@ use base_db::{FileId, FileRange};
 use hir_def::{
     body, macro_id_to_def_id,
     resolver::{self, HasResolver, Resolver, TypeNs},
+    type_ref::Mutability,
     AsMacroCall, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
@@ -313,6 +314,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
         self.imp.resolve_type(ty)
     }
 
+    // FIXME: Figure out a nice interface to inspect adjustments
+    pub fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
+        self.imp.is_implicit_reborrow(expr)
+    }
+
     pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
         self.imp.type_of_expr(expr)
     }
@@ -900,6 +906,10 @@ impl<'db> SemanticsImpl<'db> {
         Type::new_with_resolver(self.db, &scope.resolver, ty)
     }
 
+    fn is_implicit_reborrow(&self, expr: &ast::Expr) -> Option<Mutability> {
+        self.analyze(expr.syntax()).is_implicit_reborrow(self.db, expr)
+    }
+
     fn type_of_expr(&self, expr: &ast::Expr) -> Option<TypeInfo> {
         self.analyze(expr.syntax())
             .type_of_expr(self.db, expr)
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 499817b6b8a..576d063c432 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -20,12 +20,14 @@ use hir_def::{
     macro_id_to_def_id,
     path::{ModPath, Path, PathKind},
     resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
+    type_ref::Mutability,
     AsMacroCall, DefWithBodyId, FieldId, FunctionId, LocalFieldId, ModuleDefId, VariantId,
 };
 use hir_expand::{hygiene::Hygiene, name::AsName, HirFileId, InFile};
 use hir_ty::{
     diagnostics::{record_literal_missing_fields, record_pattern_missing_fields},
-    InferenceResult, Interner, Substitution, TyExt, TyLoweringContext,
+    Adjust, Adjustment, AutoBorrow, InferenceResult, Interner, Substitution, TyExt,
+    TyLoweringContext,
 };
 use syntax::{
     ast::{self, AstNode},
@@ -139,6 +141,23 @@ impl SourceAnalyzer {
         Some(res)
     }
 
+    pub(crate) fn is_implicit_reborrow(
+        &self,
+        db: &dyn HirDatabase,
+        expr: &ast::Expr,
+    ) -> Option<Mutability> {
+        let expr_id = self.expr_id(db, expr)?;
+        let infer = self.infer.as_ref()?;
+        let adjustments = infer.expr_adjustments.get(&expr_id)?;
+        adjustments.windows(2).find_map(|slice| match slice {
+            &[Adjustment {kind: Adjust::Deref(None), ..}, Adjustment {kind: Adjust::Borrow(AutoBorrow::Ref(m)), ..}] => Some(match m {
+                hir_ty::Mutability::Mut => Mutability::Mut,
+                hir_ty::Mutability::Not => Mutability::Shared,
+            }),
+            _ => None,
+        })
+    }
+
     pub(crate) fn type_of_expr(
         &self,
         db: &dyn HirDatabase,
diff --git a/crates/hir_ty/src/lib.rs b/crates/hir_ty/src/lib.rs
index d6a524d5939..59e6fe2a040 100644
--- a/crates/hir_ty/src/lib.rs
+++ b/crates/hir_ty/src/lib.rs
@@ -50,7 +50,9 @@ use crate::{db::HirDatabase, utils::generics};
 pub use autoderef::autoderef;
 pub use builder::{ParamKind, TyBuilder};
 pub use chalk_ext::*;
-pub use infer::{could_unify, InferenceDiagnostic, InferenceResult};
+pub use infer::{
+    could_unify, Adjust, Adjustment, AutoBorrow, InferenceDiagnostic, InferenceResult,
+};
 pub use interner::Interner;
 pub use lower::{
     associated_type_shorthand_candidates, callable_item_sig, CallableDefId, ImplTraitLoweringMode,
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 565bee88f3f..4fdb7d0bc33 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -19,6 +19,7 @@ pub struct InlayHintsConfig {
     pub type_hints: bool,
     pub parameter_hints: bool,
     pub chaining_hints: bool,
+    pub reborrow_hints: bool,
     pub closure_return_type_hints: bool,
     pub lifetime_elision_hints: LifetimeElisionHints,
     pub param_names_for_lifetime_elision_hints: bool,
@@ -35,6 +36,7 @@ pub enum LifetimeElisionHints {
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub enum InlayKind {
+    ImplicitReborrow,
     TypeHint,
     ParameterHint,
     ClosureReturnTypeHint,
@@ -65,10 +67,7 @@ pub struct InlayHint {
 //
 // * return types of closure expressions with blocks
 // * elided lifetimes
-//
-// **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.
-// This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
-// https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
+// * compiler inserted reborrows
 //
 // |===
 // | Editor  | Action Name
@@ -116,17 +115,16 @@ fn hints(
     if let Some(expr) = ast::Expr::cast(node.clone()) {
         chaining_hints(hints, sema, &famous_defs, config, &expr);
         match expr {
-            ast::Expr::CallExpr(it) => {
-                param_name_hints(hints, sema, config, ast::Expr::from(it));
-            }
+            ast::Expr::CallExpr(it) => param_name_hints(hints, sema, config, ast::Expr::from(it)),
             ast::Expr::MethodCallExpr(it) => {
-                param_name_hints(hints, sema, config, ast::Expr::from(it));
+                param_name_hints(hints, sema, config, ast::Expr::from(it))
             }
-            ast::Expr::ClosureExpr(it) => {
-                closure_ret_hints(hints, sema, &famous_defs, config, it);
-            }
-            _ => (),
-        }
+            ast::Expr::ClosureExpr(it) => closure_ret_hints(hints, sema, &famous_defs, config, it),
+            // We could show reborrows for all expressions, but usually that is just noise to the user
+            // and the main point here is to show why "moving" a mutable reference doesn't necessarily move it
+            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::Fn::cast(node) {
@@ -365,6 +363,28 @@ fn closure_ret_hints(
     Some(())
 }
 
+fn reborrow_hints(
+    acc: &mut Vec<InlayHint>,
+    sema: &Semantics<RootDatabase>,
+    config: &InlayHintsConfig,
+    expr: &ast::Expr,
+) -> Option<()> {
+    if !config.reborrow_hints {
+        return None;
+    }
+
+    let mutability = sema.is_implicit_reborrow(expr)?;
+    acc.push(InlayHint {
+        range: expr.syntax().text_range(),
+        kind: InlayKind::ImplicitReborrow,
+        label: match mutability {
+            hir::Mutability::Shared => SmolStr::new_inline("&*"),
+            hir::Mutability::Mut => SmolStr::new_inline("&mut *"),
+        },
+    });
+    Some(())
+}
+
 fn chaining_hints(
     acc: &mut Vec<InlayHint>,
     sema: &Semantics<RootDatabase>,
@@ -834,6 +854,7 @@ mod tests {
         lifetime_elision_hints: LifetimeElisionHints::Never,
         hide_named_constructor_hints: false,
         closure_return_type_hints: false,
+        reborrow_hints: false,
         param_names_for_lifetime_elision_hints: false,
         max_length: None,
     };
@@ -841,6 +862,7 @@ mod tests {
         type_hints: true,
         parameter_hints: true,
         chaining_hints: true,
+        reborrow_hints: true,
         closure_return_type_hints: true,
         lifetime_elision_hints: LifetimeElisionHints::Always,
         ..DISABLED_CONFIG
@@ -2118,4 +2140,39 @@ impl () {
 "#,
         );
     }
+
+    #[test]
+    fn hints_implicit_reborrow() {
+        check_with_config(
+            InlayHintsConfig { reborrow_hints: true, ..DISABLED_CONFIG },
+            r#"
+fn __() {
+    let unique = &mut ();
+    let r_mov = unique;
+    let foo: &mut _ = unique;
+                    //^^^^^^ &mut *
+    ref_mut_id(unique);
+             //^^^^^^ &mut *
+    let shared = ref_id(unique);
+                      //^^^^^^ &*
+    let mov = shared;
+    let r_mov: &_ = shared;
+    ref_id(shared);
+
+    identity(unique);
+    identity(shared);
+}
+fn identity<T>(t: T) -> T {
+    t
+}
+fn ref_mut_id(x: &mut ()) -> &mut () {
+    x
+  //^ &mut *
+}
+fn ref_id(x: &()) -> &() {
+    x
+}
+"#,
+        );
+    }
 }
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 3c81bfa3a92..0980b871559 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -114,6 +114,7 @@ impl StaticIndex<'_> {
                     chaining_hints: true,
                     closure_return_type_hints: true,
                     lifetime_elision_hints: LifetimeElisionHints::Never,
+                    reborrow_hints: false,
                     hide_named_constructor_hints: false,
                     param_names_for_lifetime_elision_hints: false,
                     max_length: Some(25),
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 3afbeac47c7..ab197c671df 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -256,6 +256,8 @@ config_data! {
         inlayHints_chainingHints: bool                      = "true",
         /// Whether to show inlay type hints for return types of closures with blocks.
         inlayHints_closureReturnTypeHints: bool             = "false",
+        /// Whether to show inlay type hints for compiler inserted reborrows.
+        inlayHints_reborrowHints: bool                      = "false",
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
         inlayHints_lifetimeElisionHints: LifetimeElisionDef = "\"never\"",
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -866,6 +868,7 @@ impl Config {
                 LifetimeElisionDef::SkipTrivial => LifetimeElisionHints::SkipTrivial,
             },
             hide_named_constructor_hints: self.data.inlayHints_hideNamedConstructorHints,
+            reborrow_hints: self.data.inlayHints_reborrowHints,
             param_names_for_lifetime_elision_hints: self
                 .data
                 .inlayHints_lifetimeElisionHints_useParameterNames,
diff --git a/crates/rust-analyzer/src/to_proto.rs b/crates/rust-analyzer/src/to_proto.rs
index 693b394d3bd..7c5785bfb8b 100644
--- a/crates/rust-analyzer/src/to_proto.rs
+++ b/crates/rust-analyzer/src/to_proto.rs
@@ -426,7 +426,11 @@ pub(crate) fn inlay_hint(
             _ => inlay_hint.label.to_string(),
         }),
         position: match inlay_hint.kind {
-            InlayKind::ParameterHint => position(line_index, inlay_hint.range.start()),
+            // before annotated thing
+            InlayKind::ParameterHint | InlayKind::ImplicitReborrow => {
+                position(line_index, inlay_hint.range.start())
+            }
+            // after annotated thing
             InlayKind::ClosureReturnTypeHint
             | InlayKind::TypeHint
             | InlayKind::ChainingHint
@@ -438,7 +442,9 @@ pub(crate) fn inlay_hint(
             InlayKind::ClosureReturnTypeHint | InlayKind::TypeHint | InlayKind::ChainingHint => {
                 Some(lsp_ext::InlayHintKind::TYPE)
             }
-            InlayKind::GenericParamListHint | InlayKind::LifetimeHint => None,
+            InlayKind::GenericParamListHint
+            | InlayKind::LifetimeHint
+            | InlayKind::ImplicitReborrow => None,
         },
         tooltip: None,
         padding_left: Some(match inlay_hint.kind {
@@ -447,6 +453,7 @@ pub(crate) fn inlay_hint(
             InlayKind::ChainingHint => true,
             InlayKind::GenericParamListHint => false,
             InlayKind::LifetimeHint => false,
+            InlayKind::ImplicitReborrow => false,
         }),
         padding_right: Some(match inlay_hint.kind {
             InlayKind::TypeHint | InlayKind::ChainingHint | InlayKind::ClosureReturnTypeHint => {
@@ -455,6 +462,7 @@ pub(crate) fn inlay_hint(
             InlayKind::ParameterHint => true,
             InlayKind::LifetimeHint => true,
             InlayKind::GenericParamListHint => false,
+            InlayKind::ImplicitReborrow => false,
         }),
     }
 }
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 42f485b5111..61bd36202d9 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -378,6 +378,11 @@ Whether to show inlay type hints for method chains.
 --
 Whether to show inlay type hints for return types of closures with blocks.
 --
+[[rust-analyzer.inlayHints.reborrowHints]]rust-analyzer.inlayHints.reborrowHints (default: `false`)::
++
+--
+Whether to show inlay type hints for compiler inserted reborrows.
+--
 [[rust-analyzer.inlayHints.lifetimeElisionHints]]rust-analyzer.inlayHints.lifetimeElisionHints (default: `"never"`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index ffd89d96d4c..911fa2bd763 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -795,6 +795,11 @@
                     "default": false,
                     "type": "boolean"
                 },
+                "rust-analyzer.inlayHints.reborrowHints": {
+                    "markdownDescription": "Whether to show inlay type hints for compiler inserted reborrows.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.lifetimeElisionHints": {
                     "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
                     "default": "never",