Unnamed repository; edit this file 'description' to name the repository.
Merge pull request #18991 from Veykril/push-rmqmnrymwmoz
Keep already computed inlay hint properties instead of late resolving them
Lukas Wirth 2025-01-24
parent 3a163fa · parent 41e6a87 · commit a62e2f5
-rw-r--r--crates/hir/src/lib.rs41
-rw-r--r--crates/ide/src/inlay_hints.rs77
-rw-r--r--crates/ide/src/inlay_hints/bounds.rs32
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs301
-rw-r--r--crates/ide/src/inlay_hints/closing_brace.rs7
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs17
-rw-r--r--crates/ide/src/inlay_hints/generic_param.rs73
-rw-r--r--crates/ide/src/inlay_hints/implicit_drop.rs29
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs45
-rw-r--r--crates/ide/src/lib.rs3
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs130
11 files changed, 433 insertions, 322 deletions
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index 4938478bb1..0cbc75726b 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -45,7 +45,7 @@ use hir_def::{
body::BodyDiagnostic,
data::{adt::VariantData, TraitFlags},
generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance},
- hir::{BindingAnnotation, BindingId, ExprId, ExprOrPatId, LabelId, Pat},
+ hir::{BindingAnnotation, BindingId, Expr, ExprId, ExprOrPatId, LabelId, Pat},
item_tree::{AttrOwner, FieldParent, ItemTreeFieldId, ItemTreeNode},
lang_item::LangItemTarget,
layout::{self, ReprOptions, TargetDataLayout},
@@ -2470,20 +2470,31 @@ impl Param {
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
- let parent = match self.func {
- Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
- Callee::Closure(closure, _) => db.lookup_intern_closure(closure.into()).0,
- _ => return None,
- };
- let body = db.body(parent);
- if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
- Some(Local { parent, binding_id: self_param })
- } else if let Pat::Bind { id, .. } =
- &body[body.params[self.idx - body.self_param.is_some() as usize]]
- {
- Some(Local { parent, binding_id: *id })
- } else {
- None
+ match self.func {
+ Callee::Def(CallableDefId::FunctionId(it)) => {
+ let parent = DefWithBodyId::FunctionId(it);
+ let body = db.body(parent);
+ if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
+ Some(Local { parent, binding_id: self_param })
+ } else if let Pat::Bind { id, .. } =
+ &body[body.params[self.idx - body.self_param.is_some() as usize]]
+ {
+ Some(Local { parent, binding_id: *id })
+ } else {
+ None
+ }
+ }
+ Callee::Closure(closure, _) => {
+ let c = db.lookup_intern_closure(closure.into());
+ let body = db.body(c.0);
+ if let Expr::Closure { args, .. } = &body[c.1] {
+ if let Pat::Bind { id, .. } = &body[args[self.idx]] {
+ return Some(Local { parent: c.0, binding_id: *id });
+ }
+ }
+ None
+ }
+ _ => None,
}
}
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 6d83a747d7..1f723c85df 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -209,7 +209,7 @@ fn hints(
) {
closing_brace::hints(hints, sema, config, file_id, node.clone());
if let Some(any_has_generic_args) = ast::AnyHasGenericArgs::cast(node.clone()) {
- generic_param::hints(hints, sema, config, any_has_generic_args);
+ generic_param::hints(hints, famous_defs, config, any_has_generic_args);
}
match_ast! {
@@ -300,22 +300,23 @@ pub struct InlayHintsConfig {
pub closing_brace_hints_min_lines: Option<usize>,
pub fields_to_resolve: InlayFieldsToResolve,
}
+
impl InlayHintsConfig {
- fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> Lazy<TextEdit> {
+ fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty<TextEdit> {
if self.fields_to_resolve.resolve_text_edits {
- Lazy::Lazy
+ LazyProperty::Lazy
} else {
let edit = finish();
never!(edit.is_empty(), "inlay hint produced an empty text edit");
- Lazy::Computed(edit)
+ LazyProperty::Computed(edit)
}
}
- fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> Lazy<InlayTooltip> {
+ fn lazy_tooltip(&self, finish: impl FnOnce() -> InlayTooltip) -> LazyProperty<InlayTooltip> {
if self.fields_to_resolve.resolve_hint_tooltip
&& self.fields_to_resolve.resolve_label_tooltip
{
- Lazy::Lazy
+ LazyProperty::Lazy
} else {
let tooltip = finish();
never!(
@@ -326,7 +327,20 @@ impl InlayHintsConfig {
.is_empty(),
"inlay hint produced an empty tooltip"
);
- Lazy::Computed(tooltip)
+ LazyProperty::Computed(tooltip)
+ }
+ }
+
+ /// This always reports a resolvable location, so only use this when it is very likely for a
+ /// location link to actually resolve but where computing `finish` would be costly.
+ fn lazy_location_opt(
+ &self,
+ finish: impl FnOnce() -> Option<FileRange>,
+ ) -> Option<LazyProperty<FileRange>> {
+ if self.fields_to_resolve.resolve_label_location {
+ Some(LazyProperty::Lazy)
+ } else {
+ finish().map(LazyProperty::Computed)
}
}
}
@@ -441,7 +455,7 @@ pub struct InlayHint {
/// The actual label to show in the inlay hint.
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
- pub text_edit: Option<Lazy<TextEdit>>,
+ pub text_edit: Option<LazyProperty<TextEdit>>,
/// Range to recompute inlay hints when trying to resolve for this hint. If this is none, the
/// hint does not support resolving.
pub resolve_parent: Option<TextRange>,
@@ -449,15 +463,15 @@ pub struct InlayHint {
/// A type signaling that a value is either computed, or is available for computation.
#[derive(Clone, Debug)]
-pub enum Lazy<T> {
+pub enum LazyProperty<T> {
Computed(T),
Lazy,
}
-impl<T> Lazy<T> {
+impl<T> LazyProperty<T> {
pub fn computed(self) -> Option<T> {
match self {
- Lazy::Computed(it) => Some(it),
+ LazyProperty::Computed(it) => Some(it),
_ => None,
}
}
@@ -508,8 +522,8 @@ pub struct InlayHintLabel {
impl InlayHintLabel {
pub fn simple(
s: impl Into<String>,
- tooltip: Option<Lazy<InlayTooltip>>,
- linked_location: Option<FileRange>,
+ tooltip: Option<LazyProperty<InlayTooltip>>,
+ linked_location: Option<LazyProperty<FileRange>>,
) -> InlayHintLabel {
InlayHintLabel {
parts: smallvec![InlayHintLabelPart { text: s.into(), linked_location, tooltip }],
@@ -593,16 +607,16 @@ pub struct InlayHintLabelPart {
/// refers to (not necessarily the location itself).
/// When setting this, no tooltip must be set on the containing hint, or VS Code will display
/// them both.
- pub linked_location: Option<FileRange>,
+ pub linked_location: Option<LazyProperty<FileRange>>,
/// The tooltip to show when hovering over the inlay hint, this may invoke other actions like
/// hover requests to show.
- pub tooltip: Option<Lazy<InlayTooltip>>,
+ pub tooltip: Option<LazyProperty<InlayTooltip>>,
}
impl std::hash::Hash for InlayHintLabelPart {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.text.hash(state);
- self.linked_location.hash(state);
+ self.linked_location.is_some().hash(state);
self.tooltip.is_some().hash(state);
}
}
@@ -610,7 +624,9 @@ impl std::hash::Hash for InlayHintLabelPart {
impl fmt::Debug for InlayHintLabelPart {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
- Self { text, linked_location: None, tooltip: None | Some(Lazy::Lazy) } => text.fmt(f),
+ Self { text, linked_location: None, tooltip: None | Some(LazyProperty::Lazy) } => {
+ text.fmt(f)
+ }
Self { text, linked_location, tooltip } => f
.debug_struct("InlayHintLabelPart")
.field("text", text)
@@ -618,8 +634,10 @@ impl fmt::Debug for InlayHintLabelPart {
.field(
"tooltip",
&tooltip.as_ref().map_or("", |it| match it {
- Lazy::Computed(InlayTooltip::String(it) | InlayTooltip::Markdown(it)) => it,
- Lazy::Lazy => "",
+ LazyProperty::Computed(
+ InlayTooltip::String(it) | InlayTooltip::Markdown(it),
+ ) => it,
+ LazyProperty::Lazy => "",
}),
)
.finish(),
@@ -632,7 +650,8 @@ struct InlayHintLabelBuilder<'a> {
db: &'a RootDatabase,
result: InlayHintLabel,
last_part: String,
- location: Option<FileRange>,
+ resolve: bool,
+ location: Option<LazyProperty<FileRange>>,
}
impl fmt::Write for InlayHintLabelBuilder<'_> {
@@ -645,11 +664,16 @@ impl HirWrite for InlayHintLabelBuilder<'_> {
fn start_location_link(&mut self, def: ModuleDefId) {
never!(self.location.is_some(), "location link is already started");
self.make_new_part();
- let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
- let location = location.call_site();
- let location =
- FileRange { file_id: location.file_id, range: location.focus_or_full_range() };
- self.location = Some(location);
+
+ self.location = Some(if self.resolve {
+ LazyProperty::Lazy
+ } else {
+ LazyProperty::Computed({
+ let Some(location) = ModuleDef::from(def).try_to_nav(self.db) else { return };
+ let location = location.call_site();
+ FileRange { file_id: location.file_id, range: location.focus_or_full_range() }
+ })
+ });
}
fn end_location_link(&mut self) {
@@ -735,6 +759,7 @@ fn label_of_ty(
last_part: String::new(),
location: None,
result: InlayHintLabel::default(),
+ resolve: config.fields_to_resolve.resolve_label_location,
};
let _ = rec(sema, famous_defs, config.max_length, ty, &mut label_builder, config, edition);
let r = label_builder.finish();
@@ -783,7 +808,7 @@ fn ty_to_text_edit(
ty: &hir::Type,
offset_to_insert: TextSize,
prefix: impl Into<String>,
-) -> Option<Lazy<TextEdit>> {
+) -> Option<LazyProperty<TextEdit>> {
// FIXME: Limit the length and bail out on excess somehow?
let rendered = sema
.scope(node_for_hint)
diff --git a/crates/ide/src/inlay_hints/bounds.rs b/crates/ide/src/inlay_hints/bounds.rs
index 429ddd31cb..e9b728bcaa 100644
--- a/crates/ide/src/inlay_hints/bounds.rs
+++ b/crates/ide/src/inlay_hints/bounds.rs
@@ -22,11 +22,7 @@ pub(super) fn hints(
return None;
}
- let linked_location =
- famous_defs.core_marker_Sized().and_then(|it| it.try_to_nav(sema.db)).map(|it| {
- let n = it.call_site();
- FileRange { file_id: n.file_id, range: n.focus_or_full_range() }
- });
+ let sized_trait = famous_defs.core_marker_Sized();
for param in params.type_or_const_params() {
match param {
@@ -48,7 +44,17 @@ pub(super) fn hints(
}
hint.parts.push(InlayHintLabelPart {
text: "Sized".to_owned(),
- linked_location,
+ linked_location: sized_trait.and_then(|it| {
+ config.lazy_location_opt(|| {
+ it.try_to_nav(sema.db).map(|it| {
+ let n = it.call_site();
+ FileRange {
+ file_id: n.file_id,
+ range: n.focus_or_full_range(),
+ }
+ })
+ })
+ }),
tooltip: None,
});
if has_bounds {
@@ -134,12 +140,14 @@ fn foo<T>() {}
InlayHintLabelPart {
text: "Sized",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 135..140,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 135..140,
+ },
+ ),
),
tooltip: "",
},
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 7fa7ab1a94..8471547727 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -81,7 +81,10 @@ mod tests {
use crate::{
fixture,
- inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
+ inlay_hints::{
+ tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
+ LazyProperty,
+ },
InlayHintsConfig,
};
@@ -99,7 +102,7 @@ mod tests {
let (analysis, file_id) = fixture::file(ra_fixture);
let mut inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
inlay_hints.iter_mut().flat_map(|hint| &mut hint.label.parts).for_each(|hint| {
- if let Some(loc) = &mut hint.linked_location {
+ if let Some(LazyProperty::Computed(loc)) = &mut hint.linked_location {
loc.range = TextRange::empty(TextSize::from(0));
}
});
@@ -134,12 +137,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 63..64,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 63..64,
+ },
+ ),
),
tooltip: "",
},
@@ -151,12 +156,14 @@ fn main() {
InlayHintLabelPart {
text: "A",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 7..8,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 7..8,
+ },
+ ),
),
tooltip: "",
},
@@ -213,12 +220,14 @@ fn main() {
InlayHintLabelPart {
text: "C",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 51..52,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 51..52,
+ },
+ ),
),
tooltip: "",
},
@@ -230,12 +239,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 29..30,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 29..30,
+ },
+ ),
),
tooltip: "",
},
@@ -276,12 +287,14 @@ fn main() {
InlayHintLabelPart {
text: "C",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 51..52,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 51..52,
+ },
+ ),
),
tooltip: "",
},
@@ -293,12 +306,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 29..30,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 29..30,
+ },
+ ),
),
tooltip: "",
},
@@ -340,12 +355,14 @@ fn main() {
InlayHintLabelPart {
text: "B",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 23..24,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 23..24,
+ },
+ ),
),
tooltip: "",
},
@@ -353,12 +370,14 @@ fn main() {
InlayHintLabelPart {
text: "X",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 55..56,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 55..56,
+ },
+ ),
),
tooltip: "",
},
@@ -371,12 +390,14 @@ fn main() {
InlayHintLabelPart {
text: "A",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 7..8,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 7..8,
+ },
+ ),
),
tooltip: "",
},
@@ -384,12 +405,14 @@ fn main() {
InlayHintLabelPart {
text: "X",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 55..56,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 55..56,
+ },
+ ),
),
tooltip: "",
},
@@ -435,12 +458,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -448,12 +473,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -467,12 +494,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -480,12 +509,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -499,12 +530,14 @@ fn main() {
InlayHintLabelPart {
text: "Iterator",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -512,12 +545,14 @@ fn main() {
InlayHintLabelPart {
text: "Item",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 1,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 1,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -531,12 +566,14 @@ fn main() {
InlayHintLabelPart {
text: "MyIter",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 0..0,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 0..0,
+ },
+ ),
),
tooltip: "",
},
@@ -577,12 +614,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 7..13,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 7..13,
+ },
+ ),
),
tooltip: "",
},
@@ -594,12 +633,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 7..13,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 7..13,
+ },
+ ),
),
tooltip: "",
},
@@ -611,12 +652,14 @@ fn main() {
InlayHintLabelPart {
text: "Struct",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 7..13,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 7..13,
+ },
+ ),
),
tooltip: "",
},
@@ -628,12 +671,14 @@ fn main() {
InlayHintLabelPart {
text: "self",
linked_location: Some(
- FileRangeWrapper {
- file_id: FileId(
- 0,
- ),
- range: 42..46,
- },
+ Computed(
+ FileRangeWrapper {
+ file_id: FileId(
+ 0,
+ ),
+ range: 42..46,
+ },
+ ),
),
tooltip: "",
},
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index 90b8be64a4..3767d34e2c 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -11,7 +11,10 @@ use syntax::{
match_ast, SyntaxKind, SyntaxNode, T,
};
-use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+use crate::{
+ inlay_hints::LazyProperty, InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig,
+ InlayKind,
+};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
@@ -141,7 +144,7 @@ pub(super) fn hints(
acc.push(InlayHint {
range: closing_token.text_range(),
kind: InlayKind::ClosingBrace,
- label: InlayHintLabel::simple(label, None, linked_location),
+ label: InlayHintLabel::simple(label, None, linked_location.map(LazyProperty::Computed)),
text_edit: None,
position: InlayHintPosition::After,
pad_left: true,
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index 906f2acf0c..3e91618d08 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -53,10 +53,6 @@ pub(super) fn hints(
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
let local = capture.local();
- let source = local.primary_source(sema.db);
-
- // force cache the source file, otherwise sema lookup will potentially panic
- _ = sema.parse_or_expand(source.file());
let label = format!(
"{}{}",
@@ -73,8 +69,17 @@ pub(super) fn hints(
}
hint.label.append_part(InlayHintLabelPart {
text: label,
- linked_location: source.name().and_then(|name| {
- name.syntax().original_file_range_opt(sema.db).map(TupleExt::head).map(Into::into)
+ linked_location: config.lazy_location_opt(|| {
+ let source = local.primary_source(sema.db);
+
+ // force cache the source file, otherwise sema lookup will potentially panic
+ _ = sema.parse_or_expand(source.file());
+ source.name().and_then(|name| {
+ name.syntax()
+ .original_file_range_opt(sema.db)
+ .map(TupleExt::head)
+ .map(Into::into)
+ })
}),
tooltip: None,
});
diff --git a/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs
index 037b328d97..762a4c2655 100644
--- a/crates/ide/src/inlay_hints/generic_param.rs
+++ b/crates/ide/src/inlay_hints/generic_param.rs
@@ -1,17 +1,19 @@
//! Implementation of inlay hints for generic parameters.
-use ide_db::{active_parameter::generic_def_for_node, RootDatabase};
+use ide_db::{active_parameter::generic_def_for_node, famous_defs::FamousDefs};
use syntax::{
ast::{self, AnyHasGenericArgs, HasGenericArgs, HasName},
AstNode,
};
-use crate::{inlay_hints::GenericParameterHints, InlayHint, InlayHintsConfig, InlayKind};
+use crate::{
+ inlay_hints::GenericParameterHints, InlayHint, InlayHintLabel, InlayHintsConfig, InlayKind,
+};
-use super::param_name::{is_argument_similar_to_param_name, render_label};
+use super::param_name::is_argument_similar_to_param_name;
pub(crate) fn hints(
acc: &mut Vec<InlayHint>,
- sema: &hir::Semantics<'_, RootDatabase>,
+ FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
node: AnyHasGenericArgs,
) -> Option<()> {
@@ -45,12 +47,23 @@ pub(crate) fn hints(
return None;
}
- let name = param.name(sema.db);
- let param_name = name.as_str();
+ let allowed = match (param, &arg) {
+ (hir::GenericParam::TypeParam(_), ast::GenericArg::TypeArg(_)) => type_hints,
+ (hir::GenericParam::ConstParam(_), ast::GenericArg::ConstArg(_)) => const_hints,
+ (hir::GenericParam::LifetimeParam(_), ast::GenericArg::LifetimeArg(_)) => {
+ lifetime_hints
+ }
+ _ => false,
+ };
+ if !allowed {
+ return None;
+ }
+
+ let param_name = param.name(sema.db);
let should_hide = {
let argument = get_string_representation(&arg)?;
- is_argument_similar_to_param_name(&argument, param_name)
+ is_argument_similar_to_param_name(&argument, param_name.as_str())
};
if should_hide {
@@ -59,30 +72,28 @@ pub(crate) fn hints(
let range = sema.original_range_opt(arg.syntax())?.range;
- let source_syntax = match param {
- hir::GenericParam::TypeParam(it) => {
- if !type_hints || !matches!(arg, ast::GenericArg::TypeArg(_)) {
- return None;
- }
- sema.source(it.merge())?.value.syntax().clone()
- }
- hir::GenericParam::ConstParam(it) => {
- if !const_hints || !matches!(arg, ast::GenericArg::ConstArg(_)) {
- return None;
- }
- let syntax = sema.source(it.merge())?.value.syntax().clone();
- let const_param = ast::ConstParam::cast(syntax)?;
- const_param.name()?.syntax().clone()
- }
- hir::GenericParam::LifetimeParam(it) => {
- if !lifetime_hints || !matches!(arg, ast::GenericArg::LifetimeArg(_)) {
- return None;
- }
- sema.source(it)?.value.syntax().clone()
- }
- };
- let linked_location = sema.original_range_opt(&source_syntax);
- let label = render_label(param_name, config, linked_location);
+ let colon = if config.render_colons { ":" } else { "" };
+ let label = InlayHintLabel::simple(
+ format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
+ None,
+ config.lazy_location_opt(|| {
+ let source_syntax = match param {
+ hir::GenericParam::TypeParam(it) => {
+ sema.source(it.merge()).map(|it| it.value.syntax().clone())
+ }
+ hir::GenericParam::ConstParam(it) => {
+ let syntax = sema.source(it.merge())?.value.syntax().clone();
+ let const_param = ast::ConstParam::cast(syntax)?;
+ const_param.name().map(|it| it.syntax().clone())
+ }
+ hir::GenericParam::LifetimeParam(it) => {
+ sema.source(it).map(|it| it.value.syntax().clone())
+ }
+ };
+ let linked_location = source_syntax.and_then(|it| sema.original_range_opt(&it));
+ linked_location.map(Into::into)
+ }),
+ );
Some(InlayHint {
range,
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index 1358d3722f..27c7c3d498 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -49,7 +49,7 @@ pub(super) fn hints(
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 {
+ let Some(&binding_idx) = local_to_binding.get(place.local) else {
continue; // Ignore temporary values
};
let range = match terminator.span {
@@ -91,25 +91,26 @@ pub(super) fn hints(
},
MirSpan::Unknown => continue,
};
- let binding_source = source_map
- .patterns_for_binding(*binding)
- .first()
- .and_then(|d| source_map.pat_syntax(*d).ok())
- .and_then(|d| {
- Some(FileRange {
- file_id: d.file_id.file_id()?.into(),
- range: d.value.text_range(),
- })
- });
- let binding = &hir.bindings[*binding];
+ let binding = &hir.bindings[binding_idx];
let name = binding.name.display_no_db(file_id.edition()).to_smolstr();
if name.starts_with("<ra@") {
continue; // Ignore desugared variables
}
let mut label = InlayHintLabel::simple(
name,
- Some(config.lazy_tooltip(|| crate::InlayTooltip::String("moz".into()))),
- binding_source,
+ None,
+ config.lazy_location_opt(|| {
+ source_map
+ .patterns_for_binding(binding_idx)
+ .first()
+ .and_then(|d| source_map.pat_syntax(*d).ok())
+ .and_then(|d| {
+ Some(FileRange {
+ file_id: d.file_id.file_id()?.into(),
+ range: d.value.text_range(),
+ })
+ })
+ }),
);
label.prepend_str("drop(");
label.append_str(")");
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index a6f7e0c184..8f01b1bd38 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -3,7 +3,6 @@
//! fn max(x: i32, y: i32) -> i32 { x + y }
//! _ = max(/*x*/4, /*y*/4);
//! ```
-use std::fmt::Display;
use either::Either;
use hir::{Callable, Semantics};
@@ -20,7 +19,7 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
- FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ FamousDefs(sema, krate): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
_file_id: EditionedFileId,
expr: ast::Expr,
@@ -37,23 +36,29 @@ pub(super) fn hints(
.filter_map(|(p, arg)| {
// Only annotate hints for expressions that exist in the original file
let range = sema.original_range_opt(arg.syntax())?;
- let source = sema.source(p)?;
- let (param_name, name_syntax) = match source.value.as_ref() {
- Either::Left(pat) => (pat.name()?, pat.name()),
- Either::Right(param) => match param.pat()? {
- ast::Pat::IdentPat(it) => (it.name()?, it.name()),
- _ => return None,
- },
- };
- Some((name_syntax, param_name, arg, range))
+ let param_name = p.name(sema.db)?;
+ Some((p, param_name, arg, range))
})
.filter(|(_, param_name, arg, _)| {
- !should_hide_param_name_hint(sema, &callable, &param_name.text(), arg)
+ !should_hide_param_name_hint(sema, &callable, param_name.as_str(), arg)
})
.map(|(param, param_name, _, hir::FileRange { range, .. })| {
- let linked_location = param.and_then(|name| sema.original_range_opt(name.syntax()));
-
- let label = render_label(&param_name, config, linked_location);
+ let colon = if config.render_colons { ":" } else { "" };
+ let label = InlayHintLabel::simple(
+ format!("{}{colon}", param_name.display(sema.db, krate.edition(sema.db))),
+ None,
+ config.lazy_location_opt(|| {
+ let source = sema.source(param)?;
+ let name_syntax = match source.value.as_ref() {
+ Either::Left(pat) => pat.name(),
+ Either::Right(param) => match param.pat()? {
+ ast::Pat::IdentPat(it) => it.name(),
+ _ => None,
+ },
+ }?;
+ sema.original_range_opt(name_syntax.syntax()).map(Into::into)
+ }),
+ );
InlayHint {
range,
kind: InlayKind::Parameter,
@@ -70,16 +75,6 @@ pub(super) fn hints(
Some(())
}
-pub(super) fn render_label(
- param_name: impl Display,
- config: &InlayHintsConfig,
- linked_location: Option<hir::FileRange>,
-) -> InlayHintLabel {
- let colon = if config.render_colons { ":" } else { "" };
-
- InlayHintLabel::simple(format!("{param_name}{colon}"), None, linked_location.map(Into::into))
-}
-
fn get_callable(
sema: &Semantics<'_, RootDatabase>,
expr: &ast::Expr,
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index 346e2862b0..41a3302c09 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -91,7 +91,8 @@ pub use crate::{
inlay_hints::{
AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints,
GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart,
- InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LifetimeElisionHints,
+ InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty,
+ LifetimeElisionHints,
},
join_lines::JoinLinesConfig,
markup::Markup,
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index a5516e7f9d..2a7d95d560 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -11,8 +11,8 @@ use ide::{
Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
- InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
- NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
+ InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, LazyProperty,
+ Markup, NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
};
use ide_db::{assists, rust_doc::format_docs, FxHasher};
@@ -549,12 +549,11 @@ pub(crate) fn inlay_hint(
) -> Cancellable<lsp_types::InlayHint> {
let hint_needs_resolve = |hint: &InlayHint| -> Option<TextRange> {
hint.resolve_parent.filter(|_| {
- hint.text_edit.is_some()
- || hint
- .label
- .parts
- .iter()
- .any(|part| part.linked_location.is_some() || part.tooltip.is_some())
+ hint.text_edit.as_ref().is_some_and(LazyProperty::is_lazy)
+ || hint.label.parts.iter().any(|part| {
+ part.linked_location.as_ref().is_some_and(LazyProperty::is_lazy)
+ || part.tooltip.as_ref().is_some_and(LazyProperty::is_lazy)
+ })
})
};
@@ -569,22 +568,21 @@ pub(crate) fn inlay_hint(
});
let mut something_to_resolve = false;
- let text_edits = if snap
- .config
- .visual_studio_code_version()
- .is_none_or(|version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
- && resolve_range_and_hash.is_some()
- && fields_to_resolve.resolve_text_edits
- {
- something_to_resolve |= inlay_hint.text_edit.is_some();
- None
- } else {
- inlay_hint
- .text_edit
- .take()
- .and_then(|it| it.computed())
- .map(|it| text_edit_vec(line_index, it))
- };
+ let text_edits = inlay_hint
+ .text_edit
+ .take()
+ .and_then(|it| match it {
+ LazyProperty::Computed(it) => Some(it),
+ LazyProperty::Lazy => {
+ something_to_resolve |=
+ snap.config.visual_studio_code_version().is_none_or(|version| {
+ VersionReq::parse(">=1.86.0").unwrap().matches(version)
+ }) && resolve_range_and_hash.is_some()
+ && fields_to_resolve.resolve_text_edits;
+ None
+ }
+ })
+ .map(|it| text_edit_vec(line_index, it));
let (label, tooltip) = inlay_hint_label(
snap,
fields_to_resolve,
@@ -637,22 +635,23 @@ fn inlay_hint_label(
let (label, tooltip) = match &*label.parts {
[InlayHintLabelPart { linked_location: None, .. }] => {
let InlayHintLabelPart { text, tooltip, .. } = label.parts.pop().unwrap();
- let hint_tooltip = if needs_resolve && fields_to_resolve.resolve_hint_tooltip {
- *something_to_resolve |= tooltip.is_some();
- None
- } else {
- match tooltip.and_then(|it| it.computed()) {
- Some(ide::InlayTooltip::String(s)) => {
- Some(lsp_types::InlayHintTooltip::String(s))
- }
- Some(ide::InlayTooltip::Markdown(s)) => {
- Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
- kind: lsp_types::MarkupKind::Markdown,
- value: s,
- }))
- }
- None => None,
+ let tooltip = tooltip.and_then(|it| match it {
+ LazyProperty::Computed(it) => Some(it),
+ LazyProperty::Lazy => {
+ *something_to_resolve |=
+ needs_resolve && fields_to_resolve.resolve_hint_tooltip;
+ None
}
+ });
+ let hint_tooltip = match tooltip {
+ Some(ide::InlayTooltip::String(s)) => Some(lsp_types::InlayHintTooltip::String(s)),
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintTooltip::MarkupContent(lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ }))
+ }
+ None => None,
};
(lsp_types::InlayHintLabel::String(text), hint_tooltip)
}
@@ -661,31 +660,38 @@ fn inlay_hint_label(
.parts
.into_iter()
.map(|part| {
- let tooltip = if needs_resolve && fields_to_resolve.resolve_label_tooltip {
- *something_to_resolve |= part.tooltip.is_some();
- None
- } else {
- match part.tooltip.and_then(|it| it.computed()) {
- Some(ide::InlayTooltip::String(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::String(s))
- }
- Some(ide::InlayTooltip::Markdown(s)) => {
- Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
- lsp_types::MarkupContent {
- kind: lsp_types::MarkupKind::Markdown,
- value: s,
- },
- ))
- }
- None => None,
+ let tooltip = part.tooltip.and_then(|it| match it {
+ LazyProperty::Computed(it) => Some(it),
+ LazyProperty::Lazy => {
+ *something_to_resolve |= fields_to_resolve.resolve_label_tooltip;
+ None
}
+ });
+ let tooltip = match tooltip {
+ Some(ide::InlayTooltip::String(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::String(s))
+ }
+ Some(ide::InlayTooltip::Markdown(s)) => {
+ Some(lsp_types::InlayHintLabelPartTooltip::MarkupContent(
+ lsp_types::MarkupContent {
+ kind: lsp_types::MarkupKind::Markdown,
+ value: s,
+ },
+ ))
+ }
+ None => None,
};
- let location = if needs_resolve && fields_to_resolve.resolve_label_location {
- *something_to_resolve |= part.linked_location.is_some();
- None
- } else {
- part.linked_location.map(|range| location(snap, range)).transpose()?
- };
+ let location = part
+ .linked_location
+ .and_then(|it| match it {
+ LazyProperty::Computed(it) => Some(it),
+ LazyProperty::Lazy => {
+ *something_to_resolve |= fields_to_resolve.resolve_label_location;
+ None
+ }
+ })
+ .map(|range| location(snap, range))
+ .transpose()?;
Ok(lsp_types::InlayHintLabelPart {
value: part.text,
tooltip,