Skip to content

feat: Add an option to hide adjustment hints outside of unsafe blocks and functions #13817

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/hir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down Expand Up @@ -1074,7 +1074,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()
}

Expand Down
70 changes: 66 additions & 4 deletions crates/hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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> {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions crates/ide/src/inlay_hints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,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,
Expand Down Expand Up @@ -343,6 +344,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,
Expand Down
97 changes: 96 additions & 1 deletion crates/ide/src/inlay_hints/adjustment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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;
}
Expand Down Expand Up @@ -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() };
//^^^^^^(
//^^^^^^&
//^^^^^^)
}
"#,
);
}
}
1 change: 1 addition & 0 deletions crates/ide/src/static_index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,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,
Expand Down
5 changes: 5 additions & 0 deletions crates/rust-analyzer/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -1224,6 +1226,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
Expand Down
5 changes: 5 additions & 0 deletions docs/user/generated_config.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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"`)::
+
--
Expand Down
5 changes: 5 additions & 0 deletions editors/code/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down