about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-12-22 09:37:00 +0000
committerbors <bors@rust-lang.org>2022-12-22 09:37:00 +0000
commiteb3963b22ec8ec7e99aeb1a2c7acb5689a2f76c5 (patch)
tree00b5b12b410e0ee25ca156a5018c393765e41916
parent271f7b44d3d71db2ec2d0cd1a8a59aa16f902d49 (diff)
parent1038db5f1d79155860bd9e1487c0df34ec542b39 (diff)
downloadrust-eb3963b22ec8ec7e99aeb1a2c7acb5689a2f76c5.tar.gz
rust-eb3963b22ec8ec7e99aeb1a2c7acb5689a2f76c5.zip
Auto merge of #13817 - WaffleLapkin:hide_adjustment_hints_outside_of_unsafe, r=Veykril
feat: Add an option to hide adjustment hints outside of `unsafe` blocks and functions

As the title suggests: this PR adds an option (namely `rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe`) that allows to hide adjustment hints outside of `unsafe` blocks and functions:

![2022-12-21_23-11](https://user-images.githubusercontent.com/38225716/208986376-d607de62-8290-4e16-b7fe-15b762dc5f60.png)

Requested by `@BoxyUwU` <3
-rw-r--r--crates/hir/src/lib.rs4
-rw-r--r--crates/hir/src/semantics.rs70
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs97
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs5
-rw-r--r--docs/user/generated_config.adoc5
-rw-r--r--editors/code/package.json5
8 files changed, 182 insertions, 7 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 7e0e0245261..aabfd170487 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -74,7 +74,7 @@ use once_cell::unsync::Lazy;
 use rustc_hash::FxHashSet;
 use stdx::{impl_from, never};
 use syntax::{
-    ast::{self, Expr, HasAttrs as _, HasDocComments, HasName},
+    ast::{self, HasAttrs as _, HasDocComments, HasName},
     AstNode, AstPtr, SmolStr, SyntaxNodePtr, TextRange, T,
 };
 
@@ -1082,7 +1082,7 @@ impl Variant {
         db.enum_data(self.parent.id).variants[self.id].variant_data.clone()
     }
 
-    pub fn value(self, db: &dyn HirDatabase) -> Option<Expr> {
+    pub fn value(self, db: &dyn HirDatabase) -> Option<ast::Expr> {
         self.source(db)?.value.expr()
     }
 
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 2ed62372ae2..569c2bfb9e6 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -5,11 +5,14 @@ mod source_to_def;
 use std::{cell::RefCell, fmt, iter, mem, ops};
 
 use base_db::{FileId, FileRange};
+use either::Either;
 use hir_def::{
-    body, macro_id_to_def_id,
+    body,
+    expr::Expr,
+    macro_id_to_def_id,
     resolver::{self, HasResolver, Resolver, TypeNs},
     type_ref::Mutability,
-    AsMacroCall, FunctionId, MacroId, TraitId, VariantId,
+    AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
 };
 use hir_expand::{
     db::AstDatabase,
@@ -438,8 +441,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     }
 
     pub fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
-        let src = self.imp.find_file(src.syntax()).with_value(src).cloned();
-        T::to_def(&self.imp, src)
+        self.imp.to_def(src)
     }
 
     pub fn to_module_def(&self, file: FileId) -> Option<Module> {
@@ -481,6 +483,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
     pub fn is_unsafe_ident_pat(&self, ident_pat: &ast::IdentPat) -> bool {
         self.imp.is_unsafe_ident_pat(ident_pat)
     }
+
+    /// Returns `true` if the `node` is inside an `unsafe` context.
+    pub fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
+        self.imp.is_inside_unsafe(expr)
+    }
 }
 
 impl<'db> SemanticsImpl<'db> {
@@ -1243,6 +1250,11 @@ impl<'db> SemanticsImpl<'db> {
         f(&mut ctx)
     }
 
+    fn to_def<T: ToDef>(&self, src: &T) -> Option<T::Def> {
+        let src = self.find_file(src.syntax()).with_value(src).cloned();
+        T::to_def(&self, src)
+    }
+
     fn to_module_def(&self, file: FileId) -> impl Iterator<Item = Module> {
         self.with_ctx(|ctx| ctx.file_to_def(file)).into_iter().map(Module::from)
     }
@@ -1458,6 +1470,56 @@ impl<'db> SemanticsImpl<'db> {
             .map(|ty| ty.original.is_packed(self.db))
             .unwrap_or(false)
     }
+
+    fn is_inside_unsafe(&self, expr: &ast::Expr) -> bool {
+        let item_or_variant = |ancestor: SyntaxNode| {
+            if ast::Item::can_cast(ancestor.kind()) {
+                ast::Item::cast(ancestor).map(Either::Left)
+            } else {
+                ast::Variant::cast(ancestor).map(Either::Right)
+            }
+        };
+        let Some(enclosing_item) = expr.syntax().ancestors().find_map(item_or_variant) else { return false };
+
+        let def = match &enclosing_item {
+            Either::Left(ast::Item::Fn(it)) if it.unsafe_token().is_some() => return true,
+            Either::Left(ast::Item::Fn(it)) => {
+                self.to_def(it).map(<_>::into).map(DefWithBodyId::FunctionId)
+            }
+            Either::Left(ast::Item::Const(it)) => {
+                self.to_def(it).map(<_>::into).map(DefWithBodyId::ConstId)
+            }
+            Either::Left(ast::Item::Static(it)) => {
+                self.to_def(it).map(<_>::into).map(DefWithBodyId::StaticId)
+            }
+            Either::Left(_) => None,
+            Either::Right(it) => self.to_def(it).map(<_>::into).map(DefWithBodyId::VariantId),
+        };
+        let Some(def) = def else { return false };
+        let enclosing_node = enclosing_item.as_ref().either(|i| i.syntax(), |v| v.syntax());
+
+        let (body, source_map) = self.db.body_with_source_map(def);
+
+        let file_id = self.find_file(expr.syntax()).file_id;
+
+        let Some(mut parent) = expr.syntax().parent() else { return false };
+        loop {
+            if &parent == enclosing_node {
+                break false;
+            }
+
+            if let Some(parent) = ast::Expr::cast(parent.clone()) {
+                if let Some(expr_id) = source_map.node_expr(InFile { file_id, value: &parent }) {
+                    if let Expr::Unsafe { .. } = body[expr_id] {
+                        break true;
+                    }
+                }
+            }
+
+            let Some(parent_) = parent.parent() else { break false };
+            parent = parent_;
+        }
+    }
 }
 
 fn macro_call_to_macro_id(
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 9aef78143d6..eb91d5cec1e 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -33,6 +33,7 @@ pub struct InlayHintsConfig {
     pub parameter_hints: bool,
     pub chaining_hints: bool,
     pub adjustment_hints: AdjustmentHints,
+    pub adjustment_hints_hide_outside_unsafe: bool,
     pub closure_return_type_hints: ClosureReturnTypeHints,
     pub binding_mode_hints: bool,
     pub lifetime_elision_hints: LifetimeElisionHints,
@@ -433,6 +434,7 @@ mod tests {
         lifetime_elision_hints: LifetimeElisionHints::Never,
         closure_return_type_hints: ClosureReturnTypeHints::Never,
         adjustment_hints: AdjustmentHints::Never,
+        adjustment_hints_hide_outside_unsafe: false,
         binding_mode_hints: false,
         hide_named_constructor_hints: false,
         hide_closure_initialization_hints: false,
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index b2bec10d05d..1c13f31cf24 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -5,7 +5,6 @@
 //! ```
 use hir::{Adjust, AutoBorrow, Mutability, OverloadedDeref, PointerCast, Safety, Semantics};
 use ide_db::RootDatabase;
-
 use syntax::ast::{self, AstNode};
 
 use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind};
@@ -16,6 +15,10 @@ pub(super) fn hints(
     config: &InlayHintsConfig,
     expr: &ast::Expr,
 ) -> Option<()> {
+    if config.adjustment_hints_hide_outside_unsafe && !sema.is_inside_unsafe(expr) {
+        return None;
+    }
+
     if config.adjustment_hints == AdjustmentHints::Never {
         return None;
     }
@@ -233,4 +236,96 @@ fn or_else() {
             "#,
         )
     }
+
+    #[test]
+    fn adjustment_hints_unsafe_only() {
+        check_with_config(
+            InlayHintsConfig {
+                adjustment_hints: AdjustmentHints::Always,
+                adjustment_hints_hide_outside_unsafe: true,
+                ..DISABLED_CONFIG
+            },
+            r#"
+unsafe fn enabled() {
+    f(&&());
+    //^^^^&
+    //^^^^*
+    //^^^^*
+}
+
+fn disabled() {
+    f(&&());
+}
+
+fn mixed() {
+    f(&&());
+
+    unsafe {
+        f(&&());
+        //^^^^&
+        //^^^^*
+        //^^^^*
+    }
+}
+
+const _: () = {
+    f(&&());
+
+    unsafe {
+        f(&&());
+        //^^^^&
+        //^^^^*
+        //^^^^*
+    }
+};
+
+static STATIC: () = {
+    f(&&());
+
+    unsafe {
+        f(&&());
+        //^^^^&
+        //^^^^*
+        //^^^^*
+    }
+};
+
+enum E {
+    Disable = { f(&&()); 0 },
+    Enable = unsafe { f(&&()); 1 },
+                      //^^^^&
+                      //^^^^*
+                      //^^^^*
+}
+
+const fn f(_: &()) {}
+            "#,
+        )
+    }
+
+    #[test]
+    fn adjustment_hints_unsafe_only_with_item() {
+        check_with_config(
+            InlayHintsConfig {
+                adjustment_hints: AdjustmentHints::Always,
+                adjustment_hints_hide_outside_unsafe: true,
+                ..DISABLED_CONFIG
+            },
+            r#"
+fn a() {
+    struct Struct;
+    impl Struct {
+        fn by_ref(&self) {}
+    }
+
+    _ = Struct.by_ref();
+
+    _ = unsafe { Struct.by_ref() };
+               //^^^^^^(
+               //^^^^^^&
+               //^^^^^^)
+}
+            "#,
+        );
+    }
 }
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 42b5951c842..a8ffc387080 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -114,6 +114,7 @@ impl StaticIndex<'_> {
                     closure_return_type_hints: crate::ClosureReturnTypeHints::WithBlock,
                     lifetime_elision_hints: crate::LifetimeElisionHints::Never,
                     adjustment_hints: crate::AdjustmentHints::Never,
+                    adjustment_hints_hide_outside_unsafe: false,
                     hide_named_constructor_hints: false,
                     hide_closure_initialization_hints: false,
                     param_names_for_lifetime_elision_hints: false,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 0bcc91eb411..cba04b00c3d 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -329,6 +329,8 @@ config_data! {
         inlayHints_closureReturnTypeHints_enable: ClosureReturnTypeHintsDef  = "\"never\"",
         /// Whether to show inlay hints for type adjustments.
         inlayHints_expressionAdjustmentHints_enable: AdjustmentHintsDef = "\"never\"",
+        /// Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
+        inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -1240,6 +1242,9 @@ impl Config {
                 },
                 AdjustmentHintsDef::Reborrow => ide::AdjustmentHints::ReborrowOnly,
             },
+            adjustment_hints_hide_outside_unsafe: self
+                .data
+                .inlayHints_expressionAdjustmentHints_hideOutsideUnsafe,
             binding_mode_hints: self.data.inlayHints_bindingModeHints_enable,
             param_names_for_lifetime_elision_hints: self
                 .data
diff --git a/docs/user/generated_config.adoc b/docs/user/generated_config.adoc
index 47511aad0fe..867fd5620fd 100644
--- a/docs/user/generated_config.adoc
+++ b/docs/user/generated_config.adoc
@@ -459,6 +459,11 @@ Whether to show inlay type hints for return types of closures.
 --
 Whether to show inlay hints for type adjustments.
 --
+[[rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe]]rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe (default: `false`)::
++
+--
+Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
+--
 [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
 +
 --
diff --git a/editors/code/package.json b/editors/code/package.json
index 5b09ee6f7da..fad67ce8031 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -975,6 +975,11 @@
                         "Only show auto borrow and dereference adjustment hints."
                     ]
                 },
+                "rust-analyzer.inlayHints.expressionAdjustmentHints.hideOutsideUnsafe": {
+                    "markdownDescription": "Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.",
+                    "default": false,
+                    "type": "boolean"
+                },
                 "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
                     "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
                     "default": "never",