Auto merge of #16000 - HKalbasi:drop-inlay-hint, r=HKalbasi

Initial support for implicit drop inlay hint

cc #15785
This commit is contained in:
bors 2023-12-01 13:36:08 +00:00
commit 6e6a0b0a3d
12 changed files with 284 additions and 25 deletions

View file

@ -269,6 +269,10 @@ impl ProjectionStore {
impl ProjectionId { impl ProjectionId {
pub const EMPTY: ProjectionId = ProjectionId(0); pub const EMPTY: ProjectionId = ProjectionId(0);
pub fn is_empty(self) -> bool {
self == ProjectionId::EMPTY
}
pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] { pub fn lookup(self, store: &ProjectionStore) -> &[PlaceElem] {
store.id_to_proj.get(&self).unwrap() store.id_to_proj.get(&self).unwrap()
} }
@ -1069,6 +1073,10 @@ pub struct MirBody {
} }
impl MirBody { impl MirBody {
pub fn local_to_binding_map(&self) -> ArenaMap<LocalId, BindingId> {
self.binding_locals.iter().map(|(it, y)| (*y, it)).collect()
}
fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) { fn walk_places(&mut self, mut f: impl FnMut(&mut Place, &mut ProjectionStore)) {
fn for_operand( fn for_operand(
op: &mut Operand, op: &mut Operand,
@ -1188,3 +1196,9 @@ pub enum MirSpan {
} }
impl_from!(ExprId, PatId for MirSpan); impl_from!(ExprId, PatId for MirSpan);
impl From<&ExprId> for MirSpan {
fn from(value: &ExprId) -> Self {
(*value).into()
}
}

View file

@ -105,9 +105,14 @@ pub enum MirLowerError {
/// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves. /// A token to ensuring that each drop scope is popped at most once, thanks to the compiler that checks moves.
struct DropScopeToken; struct DropScopeToken;
impl DropScopeToken { impl DropScopeToken {
fn pop_and_drop(self, ctx: &mut MirLowerCtx<'_>, current: BasicBlockId) -> BasicBlockId { fn pop_and_drop(
self,
ctx: &mut MirLowerCtx<'_>,
current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
std::mem::forget(self); std::mem::forget(self);
ctx.pop_drop_scope_internal(current) ctx.pop_drop_scope_internal(current, span)
} }
/// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop /// It is useful when we want a drop scope is syntaxically closed, but we don't want to execute any drop
@ -582,7 +587,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.lower_loop(current, place, *label, expr_id.into(), |this, begin| { self.lower_loop(current, place, *label, expr_id.into(), |this, begin| {
let scope = this.push_drop_scope(); let scope = this.push_drop_scope();
if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? { if let Some((_, mut current)) = this.lower_expr_as_place(begin, *body, true)? {
current = scope.pop_and_drop(this, current); current = scope.pop_and_drop(this, current, body.into());
this.set_goto(current, begin, expr_id.into()); this.set_goto(current, begin, expr_id.into());
} else { } else {
scope.pop_assume_dropped(this); scope.pop_assume_dropped(this);
@ -720,7 +725,8 @@ impl<'ctx> MirLowerCtx<'ctx> {
.ok_or(MirLowerError::ContinueWithoutLoop)?, .ok_or(MirLowerError::ContinueWithoutLoop)?,
}; };
let begin = loop_data.begin; let begin = loop_data.begin;
current = self.drop_until_scope(loop_data.drop_scope_index, current); current =
self.drop_until_scope(loop_data.drop_scope_index, current, expr_id.into());
self.set_goto(current, begin, expr_id.into()); self.set_goto(current, begin, expr_id.into());
Ok(None) Ok(None)
} }
@ -759,7 +765,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.current_loop_blocks.as_ref().unwrap().drop_scope_index, self.current_loop_blocks.as_ref().unwrap().drop_scope_index,
), ),
}; };
current = self.drop_until_scope(drop_scope, current); current = self.drop_until_scope(drop_scope, current, expr_id.into());
self.set_goto(current, end, expr_id.into()); self.set_goto(current, end, expr_id.into());
Ok(None) Ok(None)
} }
@ -773,7 +779,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None); return Ok(None);
} }
} }
current = self.drop_until_scope(0, current); current = self.drop_until_scope(0, current, expr_id.into());
self.set_terminator(current, TerminatorKind::Return, expr_id.into()); self.set_terminator(current, TerminatorKind::Return, expr_id.into());
Ok(None) Ok(None)
} }
@ -1782,7 +1788,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
return Ok(None); return Ok(None);
}; };
self.push_fake_read(c, p, expr.into()); self.push_fake_read(c, p, expr.into());
current = scope2.pop_and_drop(self, c); current = scope2.pop_and_drop(self, c, expr.into());
} }
} }
} }
@ -1793,7 +1799,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
}; };
current = c; current = c;
} }
current = scope.pop_and_drop(self, current); current = scope.pop_and_drop(self, current, span);
Ok(Some(current)) Ok(Some(current))
} }
@ -1873,9 +1879,14 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
} }
fn drop_until_scope(&mut self, scope_index: usize, mut current: BasicBlockId) -> BasicBlockId { fn drop_until_scope(
&mut self,
scope_index: usize,
mut current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() { for scope in self.drop_scopes[scope_index..].to_vec().iter().rev() {
self.emit_drop_and_storage_dead_for_scope(scope, &mut current); self.emit_drop_and_storage_dead_for_scope(scope, &mut current, span);
} }
current current
} }
@ -1891,17 +1902,22 @@ impl<'ctx> MirLowerCtx<'ctx> {
} }
/// Don't call directly /// Don't call directly
fn pop_drop_scope_internal(&mut self, mut current: BasicBlockId) -> BasicBlockId { fn pop_drop_scope_internal(
&mut self,
mut current: BasicBlockId,
span: MirSpan,
) -> BasicBlockId {
let scope = self.drop_scopes.pop().unwrap(); let scope = self.drop_scopes.pop().unwrap();
self.emit_drop_and_storage_dead_for_scope(&scope, &mut current); self.emit_drop_and_storage_dead_for_scope(&scope, &mut current, span);
current current
} }
fn pop_drop_scope_assert_finished( fn pop_drop_scope_assert_finished(
&mut self, &mut self,
mut current: BasicBlockId, mut current: BasicBlockId,
span: MirSpan,
) -> Result<BasicBlockId> { ) -> Result<BasicBlockId> {
current = self.pop_drop_scope_internal(current); current = self.pop_drop_scope_internal(current, span);
if !self.drop_scopes.is_empty() { if !self.drop_scopes.is_empty() {
implementation_error!("Mismatched count between drop scope push and pops"); implementation_error!("Mismatched count between drop scope push and pops");
} }
@ -1912,6 +1928,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
&mut self, &mut self,
scope: &DropScope, scope: &DropScope,
current: &mut Idx<BasicBlock>, current: &mut Idx<BasicBlock>,
span: MirSpan,
) { ) {
for &l in scope.locals.iter().rev() { for &l in scope.locals.iter().rev() {
if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) { if !self.result.locals[l].ty.clone().is_copy(self.db, self.owner) {
@ -1919,13 +1936,10 @@ impl<'ctx> MirLowerCtx<'ctx> {
self.set_terminator( self.set_terminator(
prev, prev,
TerminatorKind::Drop { place: l.into(), target: *current, unwind: None }, TerminatorKind::Drop { place: l.into(), target: *current, unwind: None },
MirSpan::Unknown, span,
); );
} }
self.push_statement( self.push_statement(*current, StatementKind::StorageDead(l).with_span(span));
*current,
StatementKind::StorageDead(l).with_span(MirSpan::Unknown),
);
} }
} }
} }
@ -2002,7 +2016,7 @@ pub fn mir_body_for_closure_query(
|_| true, |_| true,
)?; )?;
if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? { if let Some(current) = ctx.lower_expr_to_place(*root, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current)?; let current = ctx.pop_drop_scope_assert_finished(current, root.into())?;
ctx.set_terminator(current, TerminatorKind::Return, (*root).into()); ctx.set_terminator(current, TerminatorKind::Return, (*root).into());
} }
let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default(); let mut upvar_map: FxHashMap<LocalId, Vec<(&CapturedItem, usize)>> = FxHashMap::default();
@ -2146,7 +2160,7 @@ pub fn lower_to_mir(
ctx.lower_params_and_bindings([].into_iter(), binding_picker)? ctx.lower_params_and_bindings([].into_iter(), binding_picker)?
}; };
if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? { if let Some(current) = ctx.lower_expr_to_place(root_expr, return_slot().into(), current)? {
let current = ctx.pop_drop_scope_assert_finished(current)?; let current = ctx.pop_drop_scope_assert_finished(current, root_expr.into())?;
ctx.set_terminator(current, TerminatorKind::Return, root_expr.into()); ctx.set_terminator(current, TerminatorKind::Return, root_expr.into());
} }
Ok(ctx.result) Ok(ctx.result)

View file

@ -145,7 +145,7 @@ impl<'a> MirPrettyCtx<'a> {
let indent = mem::take(&mut self.indent); let indent = mem::take(&mut self.indent);
let mut ctx = MirPrettyCtx { let mut ctx = MirPrettyCtx {
body: &body, body: &body,
local_to_binding: body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(), local_to_binding: body.local_to_binding_map(),
result, result,
indent, indent,
..*self ..*self
@ -167,7 +167,7 @@ impl<'a> MirPrettyCtx<'a> {
} }
fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self { fn new(body: &'a MirBody, hir_body: &'a Body, db: &'a dyn HirDatabase) -> Self {
let local_to_binding = body.binding_locals.iter().map(|(it, y)| (*y, it)).collect(); let local_to_binding = body.local_to_binding_map();
MirPrettyCtx { MirPrettyCtx {
body, body,
db, db,

View file

@ -67,7 +67,7 @@ use hir_ty::{
known_const_to_ast, known_const_to_ast,
layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding}, layout::{Layout as TyLayout, RustcEnumVariantIdx, RustcFieldIdx, TagEncoding},
method_resolution::{self, TyFingerprint}, method_resolution::{self, TyFingerprint},
mir::{self, interpret_mir}, mir::interpret_mir,
primitive::UintTy, primitive::UintTy,
traits::FnTrait, traits::FnTrait,
AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg, AliasTy, CallableDefId, CallableSig, Canonical, CanonicalVarKinds, Cast, ClosureId, GenericArg,
@ -129,9 +129,10 @@ pub use {
hir_ty::{ hir_ty::{
display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite}, display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
layout::LayoutError, layout::LayoutError,
mir::MirEvalError,
PointerCast, Safety, PointerCast, Safety,
}, },
// FIXME: Properly encapsulate mir
hir_ty::{mir, Interner as ChalkTyInterner},
}; };
// These are negative re-exports: pub using these names is forbidden, they // These are negative re-exports: pub using these names is forbidden, they

View file

@ -31,6 +31,7 @@ mod discriminant;
mod fn_lifetime_fn; mod fn_lifetime_fn;
mod implicit_static; mod implicit_static;
mod param_name; mod param_name;
mod implicit_drop;
#[derive(Clone, Debug, PartialEq, Eq)] #[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig { pub struct InlayHintsConfig {
@ -45,6 +46,7 @@ pub struct InlayHintsConfig {
pub closure_return_type_hints: ClosureReturnTypeHints, pub closure_return_type_hints: ClosureReturnTypeHints,
pub closure_capture_hints: bool, pub closure_capture_hints: bool,
pub binding_mode_hints: bool, pub binding_mode_hints: bool,
pub implicit_drop_hints: bool,
pub lifetime_elision_hints: LifetimeElisionHints, pub lifetime_elision_hints: LifetimeElisionHints,
pub param_names_for_lifetime_elision_hints: bool, pub param_names_for_lifetime_elision_hints: bool,
pub hide_named_constructor_hints: bool, pub hide_named_constructor_hints: bool,
@ -124,6 +126,7 @@ pub enum InlayKind {
Lifetime, Lifetime,
Parameter, Parameter,
Type, Type,
Drop,
} }
#[derive(Debug)] #[derive(Debug)]
@ -503,7 +506,10 @@ fn hints(
ast::Item(it) => match it { ast::Item(it) => match it {
// FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
ast::Item::Impl(_) => None, ast::Item::Impl(_) => None,
ast::Item::Fn(it) => fn_lifetime_fn::hints(hints, config, it), ast::Item::Fn(it) => {
implicit_drop::hints(hints, sema, config, &it);
fn_lifetime_fn::hints(hints, config, it)
},
// static type elisions // static type elisions
ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)), ast::Item::Static(it) => implicit_static::hints(hints, config, Either::Left(it)),
ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)), ast::Item::Const(it) => implicit_static::hints(hints, config, Either::Right(it)),
@ -591,6 +597,7 @@ mod tests {
max_length: None, max_length: None,
closing_brace_hints_min_lines: None, closing_brace_hints_min_lines: None,
fields_to_resolve: InlayFieldsToResolve::empty(), fields_to_resolve: InlayFieldsToResolve::empty(),
implicit_drop_hints: false,
}; };
pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig { pub(super) const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
type_hints: true, type_hints: true,

View file

@ -0,0 +1,204 @@
//! Implementation of "implicit drop" inlay hints:
//! ```no_run
//! fn main() {
//! let x = vec![2];
//! if some_condition() {
//! /* drop(x) */return;
//! }
//! }
//! ```
use hir::{
db::{DefDatabase as _, HirDatabase as _},
mir::{MirSpan, TerminatorKind},
ChalkTyInterner, DefWithBody, Semantics,
};
use ide_db::{base_db::FileRange, RootDatabase};
use syntax::{
ast::{self, AstNode},
match_ast,
};
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
config: &InlayHintsConfig,
def: &ast::Fn,
) -> Option<()> {
if !config.implicit_drop_hints {
return None;
}
let def = sema.to_def(def)?;
let def: DefWithBody = def.into();
let source_map = sema.db.body_with_source_map(def.into()).1;
let hir = sema.db.body(def.into());
let mir = sema.db.mir_body(def.into()).ok()?;
let local_to_binding = mir.local_to_binding_map();
for (_, bb) in mir.basic_blocks.iter() {
let terminator = bb.terminator.as_ref()?;
if let TerminatorKind::Drop { place, .. } = terminator.kind {
if !place.projection.is_empty() {
continue; // Ignore complex cases for now
}
if mir.locals[place.local].ty.adt_id(ChalkTyInterner).is_none() {
continue; // Arguably only ADTs have significant drop impls
}
let Some(binding) = local_to_binding.get(place.local) else {
continue; // Ignore temporary values
};
let range = match terminator.span {
MirSpan::ExprId(e) => match source_map.expr_syntax(e) {
Ok(s) => {
let root = &s.file_syntax(sema.db);
let expr = s.value.to_node(root);
let expr = expr.syntax();
match_ast! {
match expr {
ast::BlockExpr(x) => x.stmt_list().and_then(|x| x.r_curly_token()).map(|x| x.text_range()).unwrap_or_else(|| expr.text_range()),
_ => expr.text_range(),
}
}
}
Err(_) => continue,
},
MirSpan::PatId(p) => match source_map.pat_syntax(p) {
Ok(s) => s.value.text_range(),
Err(_) => continue,
},
MirSpan::Unknown => continue,
};
let binding = &hir.bindings[*binding];
let binding_source = binding
.definitions
.first()
.and_then(|d| source_map.pat_syntax(*d).ok())
.and_then(|d| {
Some(FileRange { file_id: d.file_id.file_id()?, range: d.value.text_range() })
});
let name = binding.name.to_smol_str();
if name.starts_with("<ra@") {
continue; // Ignore desugared variables
}
let mut label = InlayHintLabel::simple(
name,
Some(crate::InlayTooltip::String("moz".into())),
binding_source,
);
label.prepend_str("drop(");
label.append_str(")");
acc.push(InlayHint {
range,
position: InlayHintPosition::Before,
pad_left: true,
pad_right: true,
kind: InlayKind::Drop,
needs_resolve: label.needs_resolve(),
label,
text_edit: None,
})
}
}
Some(())
}
#[cfg(test)]
mod tests {
use crate::{
inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
InlayHintsConfig,
};
const ONLY_DROP_CONFIG: InlayHintsConfig =
InlayHintsConfig { implicit_drop_hints: true, ..DISABLED_CONFIG };
#[test]
fn basic() {
check_with_config(
ONLY_DROP_CONFIG,
r#"
struct X;
fn f() {
let x = X;
if 2 == 5 {
return;
//^^^^^^ drop(x)
}
}
//^ drop(x)
"#,
);
}
#[test]
fn no_hint_for_copy_types_and_mutable_references() {
// `T: Copy` and `T = &mut U` types do nothing on drop, so we should hide drop inlay hint for them.
check_with_config(
ONLY_DROP_CONFIG,
r#"
//- minicore: copy, derive
struct X(i32, i32);
#[derive(Clone, Copy)]
struct Y(i32, i32);
fn f() {
let a = 2;
let b = a + 4;
let mut x = X(a, b);
let mut y = Y(a, b);
let mx = &mut x;
let my = &mut y;
let c = a + b;
}
//^ drop(x)
"#,
);
}
#[test]
fn try_operator() {
// We currently show drop inlay hint for every `?` operator that may potentialy drop something. We probably need to
// make it configurable as it doesn't seem very useful.
check_with_config(
ONLY_DROP_CONFIG,
r#"
//- minicore: copy, try, option
struct X;
fn f() -> Option<()> {
let x = X;
let t_opt = Some(2);
let t = t_opt?;
//^^^^^^ drop(x)
Some(())
}
//^ drop(x)
"#,
);
}
#[test]
fn if_let() {
check_with_config(
ONLY_DROP_CONFIG,
r#"
struct X;
fn f() {
let x = X;
if let X = x {
let y = X;
}
//^ drop(y)
}
//^ drop(x)
"#,
);
}
}

View file

@ -118,6 +118,7 @@ impl StaticIndex<'_> {
adjustment_hints: crate::AdjustmentHints::Never, adjustment_hints: crate::AdjustmentHints::Never,
adjustment_hints_mode: AdjustmentHintsMode::Prefix, adjustment_hints_mode: AdjustmentHintsMode::Prefix,
adjustment_hints_hide_outside_unsafe: false, adjustment_hints_hide_outside_unsafe: false,
implicit_drop_hints: false,
hide_named_constructor_hints: false, hide_named_constructor_hints: false,
hide_closure_initialization_hints: false, hide_closure_initialization_hints: false,
closure_style: hir::ClosureStyle::ImplFn, closure_style: hir::ClosureStyle::ImplFn,

View file

@ -783,6 +783,7 @@ impl flags::AnalysisStats {
closure_return_type_hints: ide::ClosureReturnTypeHints::Always, closure_return_type_hints: ide::ClosureReturnTypeHints::Always,
closure_capture_hints: true, closure_capture_hints: true,
binding_mode_hints: true, binding_mode_hints: true,
implicit_drop_hints: true,
lifetime_elision_hints: ide::LifetimeElisionHints::Always, lifetime_elision_hints: ide::LifetimeElisionHints::Always,
param_names_for_lifetime_elision_hints: true, param_names_for_lifetime_elision_hints: true,
hide_named_constructor_hints: false, hide_named_constructor_hints: false,

View file

@ -381,6 +381,8 @@ config_data! {
inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false", inlayHints_expressionAdjustmentHints_hideOutsideUnsafe: bool = "false",
/// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). /// Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"", inlayHints_expressionAdjustmentHints_mode: AdjustmentHintsModeDef = "\"prefix\"",
/// Whether to show implicit drop hints.
inlayHints_implicitDrops_enable: bool = "false",
/// Whether to show inlay type hints for elided lifetimes in function signatures. /// Whether to show inlay type hints for elided lifetimes in function signatures.
inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"", inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = "\"never\"",
/// Whether to prefer using parameter names as the name for elided lifetime hints if possible. /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@ -1391,6 +1393,7 @@ impl Config {
type_hints: self.data.inlayHints_typeHints_enable, type_hints: self.data.inlayHints_typeHints_enable,
parameter_hints: self.data.inlayHints_parameterHints_enable, parameter_hints: self.data.inlayHints_parameterHints_enable,
chaining_hints: self.data.inlayHints_chainingHints_enable, chaining_hints: self.data.inlayHints_chainingHints_enable,
implicit_drop_hints: self.data.inlayHints_implicitDrops_enable,
discriminant_hints: match self.data.inlayHints_discriminantHints_enable { discriminant_hints: match self.data.inlayHints_discriminantHints_enable {
DiscriminantHintsDef::Always => ide::DiscriminantHints::Always, DiscriminantHintsDef::Always => ide::DiscriminantHints::Always,
DiscriminantHintsDef::Never => ide::DiscriminantHints::Never, DiscriminantHintsDef::Never => ide::DiscriminantHints::Never,

View file

@ -1054,6 +1054,10 @@ pub mod option {
Some(T), Some(T),
} }
// region:copy
impl<T: Copy> Copy for Option<T> {}
// endregion:copy
impl<T> Option<T> { impl<T> Option<T> {
pub const fn unwrap(self) -> T { pub const fn unwrap(self) -> T {
match self { match self {

View file

@ -564,6 +564,11 @@ Whether to hide inlay hints for type adjustments outside of `unsafe` blocks.
-- --
Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc). Whether to show inlay hints as postfix ops (`.*` instead of `*`, etc).
-- --
[[rust-analyzer.inlayHints.implicitDrops.enable]]rust-analyzer.inlayHints.implicitDrops.enable (default: `false`)::
+
--
Whether to show implicit drop hints.
--
[[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`):: [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
+ +
-- --

View file

@ -1264,6 +1264,11 @@
"Show prefix or postfix depending on which uses less parenthesis, preferring postfix." "Show prefix or postfix depending on which uses less parenthesis, preferring postfix."
] ]
}, },
"rust-analyzer.inlayHints.implicitDrops.enable": {
"markdownDescription": "Whether to show implicit drop hints.",
"default": false,
"type": "boolean"
},
"rust-analyzer.inlayHints.lifetimeElisionHints.enable": { "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
"markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.", "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
"default": "never", "default": "never",