Unnamed repository; edit this file 'description' to name the repository.
Improve inlay hint resolution reliability
Lukas Wirth 2024-08-30
parent 13ac53e · commit 5ca5d52
-rw-r--r--crates/ide/src/inlay_hints.rs398
-rw-r--r--crates/ide/src/inlay_hints/adjustment.rs6
-rw-r--r--crates/ide/src/inlay_hints/bind_pat.rs1
-rw-r--r--crates/ide/src/inlay_hints/binding_mode.rs10
-rw-r--r--crates/ide/src/inlay_hints/chaining.rs1
-rw-r--r--crates/ide/src/inlay_hints/closing_brace.rs4
-rw-r--r--crates/ide/src/inlay_hints/closure_captures.rs5
-rw-r--r--crates/ide/src/inlay_hints/closure_ret.rs1
-rw-r--r--crates/ide/src/inlay_hints/discriminant.rs4
-rw-r--r--crates/ide/src/inlay_hints/fn_lifetime_fn.rs8
-rw-r--r--crates/ide/src/inlay_hints/generic_param.rs1
-rw-r--r--crates/ide/src/inlay_hints/implicit_drop.rs11
-rw-r--r--crates/ide/src/inlay_hints/implicit_static.rs5
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs7
-rw-r--r--crates/ide/src/inlay_hints/range_exclusive.rs5
-rw-r--r--crates/ide/src/lib.rs4
-rw-r--r--crates/rust-analyzer/src/handlers/request.rs4
-rw-r--r--crates/rust-analyzer/src/lsp/ext.rs1
-rw-r--r--crates/rust-analyzer/src/lsp/to_proto.rs20
-rw-r--r--docs/dev/lsp-extensions.md2
20 files changed, 282 insertions, 216 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 6a5d5e26a4..93dd56a450 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -15,7 +15,7 @@ use span::{Edition, EditionedFileId};
use stdx::never;
use syntax::{
ast::{self, AstNode},
- match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize,
+ match_ast, NodeOrToken, SyntaxNode, TextRange, TextSize, WalkEvent,
};
use text_edit::TextEdit;
@@ -36,6 +36,192 @@ mod implicit_static;
mod param_name;
mod range_exclusive;
+// Feature: Inlay Hints
+//
+// rust-analyzer shows additional information inline with the source code.
+// Editors usually render this using read-only virtual text snippets interspersed with code.
+//
+// rust-analyzer by default shows hints for
+//
+// * types of local variables
+// * names of function arguments
+// * names of const generic parameters
+// * types of chained expressions
+//
+// Optionally, one can enable additional hints for
+//
+// * return types of closure expressions
+// * elided lifetimes
+// * compiler inserted reborrows
+// * names of generic type and lifetime parameters
+//
+// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
+// any of the
+// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
+// are met:
+//
+// * the parameter name is a suffix of the function's name
+// * the argument is a qualified constructing or call expression where the qualifier is an ADT
+// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
+// of argument with _ splitting it off
+// * the parameter name starts with `ra_fixture`
+// * the parameter name is a
+// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
+// in a unary function
+// * the parameter name is a
+// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
+// in a unary function
+//
+// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
+pub(crate) fn inlay_hints(
+ db: &RootDatabase,
+ file_id: FileId,
+ range_limit: Option<TextRange>,
+ config: &InlayHintsConfig,
+) -> Vec<InlayHint> {
+ let _p = tracing::info_span!("inlay_hints").entered();
+ let sema = Semantics::new(db);
+ let file_id = sema
+ .attach_first_edition(file_id)
+ .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
+ let file = sema.parse(file_id);
+ let file = file.syntax();
+
+ let mut acc = Vec::new();
+
+ let Some(scope) = sema.scope(file) else {
+ return acc;
+ };
+ let famous_defs = FamousDefs(&sema, scope.krate());
+
+ let parent_impl = &mut None;
+ let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node);
+ match range_limit {
+ // FIXME: This can miss some hints that require the parent of the range to calculate
+ Some(range) => match file.covering_element(range) {
+ NodeOrToken::Token(_) => return acc,
+ NodeOrToken::Node(n) => n
+ .preorder()
+ .filter(|event| matches!(event, WalkEvent::Enter(node) if range.intersect(node.text_range()).is_some()))
+ .for_each(hints),
+ },
+ None => file.preorder().for_each(hints),
+ };
+
+ acc
+}
+
+pub(crate) fn inlay_hints_resolve(
+ db: &RootDatabase,
+ file_id: FileId,
+ resolve_range: TextRange,
+ hash: u64,
+ config: &InlayHintsConfig,
+ hasher: impl Fn(&InlayHint) -> u64,
+) -> Option<InlayHint> {
+ let _p = tracing::info_span!("inlay_hints_resolve").entered();
+ let sema = Semantics::new(db);
+ let file_id = sema
+ .attach_first_edition(file_id)
+ .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
+ let file = sema.parse(file_id);
+ let file = file.syntax();
+
+ let scope = sema.scope(file)?;
+ let famous_defs = FamousDefs(&sema, scope.krate());
+ let mut acc = Vec::new();
+
+ let parent_impl = &mut None;
+ let hints = |node| hints(&mut acc, parent_impl, &famous_defs, config, file_id, node);
+
+ let mut res = file.clone();
+ let res = loop {
+ res = match res.child_or_token_at_range(resolve_range) {
+ Some(NodeOrToken::Node(n)) if n.text_range() == resolve_range => break n,
+ Some(NodeOrToken::Node(n)) => n,
+ _ => break res,
+ };
+ };
+ res.preorder().for_each(hints);
+ acc.into_iter().find(|hint| hasher(hint) == hash)
+}
+
+fn hints(
+ hints: &mut Vec<InlayHint>,
+ parent_impl: &mut Option<ast::Impl>,
+ famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+ config: &InlayHintsConfig,
+ file_id: EditionedFileId,
+ node: WalkEvent<SyntaxNode>,
+) {
+ let node = match node {
+ WalkEvent::Enter(node) => node,
+ WalkEvent::Leave(n) => {
+ if ast::Impl::can_cast(n.kind()) {
+ parent_impl.take();
+ }
+ return;
+ }
+ };
+ 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);
+ }
+
+ match_ast! {
+ match node {
+ ast::Expr(expr) => {
+ chaining::hints(hints, famous_defs, config, file_id, &expr);
+ adjustment::hints(hints, famous_defs, config, file_id, &expr);
+ match expr {
+ ast::Expr::CallExpr(it) => param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it)),
+ ast::Expr::MethodCallExpr(it) => {
+ param_name::hints(hints, famous_defs, config, file_id, ast::Expr::from(it))
+ }
+ ast::Expr::ClosureExpr(it) => {
+ closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
+ closure_ret::hints(hints, famous_defs, config, file_id, it)
+ },
+ ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, famous_defs, config, file_id, it),
+ _ => None,
+ }
+ },
+ ast::Pat(it) => {
+ binding_mode::hints(hints, famous_defs, config, file_id, &it);
+ match it {
+ ast::Pat::IdentPat(it) => {
+ bind_pat::hints(hints, famous_defs, config, file_id, &it);
+ }
+ ast::Pat::RangePat(it) => {
+ range_exclusive::hints(hints, famous_defs, config, file_id, it);
+ }
+ _ => {}
+ }
+ Some(())
+ },
+ ast::Item(it) => match it {
+ // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
+ ast::Item::Impl(impl_) => {
+ *parent_impl = Some(impl_);
+ None
+ },
+ ast::Item::Fn(it) => {
+ implicit_drop::hints(hints, famous_defs, config, file_id, &it);
+ fn_lifetime_fn::hints(hints, famous_defs, config, file_id, it)
+ },
+ // static type elisions
+ ast::Item::Static(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Left(it)),
+ ast::Item::Const(it) => implicit_static::hints(hints, famous_defs, config, file_id, Either::Right(it)),
+ ast::Item::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
+ _ => None,
+ },
+ // FIXME: fn-ptr type, dyn fn type, and trait object type elisions
+ ast::Type(_) => None,
+ _ => None,
+ }
+ };
+}
+
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InlayHintsConfig {
pub render_colons: bool,
@@ -162,6 +348,9 @@ pub struct InlayHint {
pub label: InlayHintLabel,
/// Text edit to apply when "accepting" this inlay hint.
pub text_edit: Option<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>,
}
impl std::hash::Hash for InlayHint {
@@ -186,6 +375,7 @@ impl InlayHint {
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: None,
}
}
@@ -198,11 +388,12 @@ impl InlayHint {
position: InlayHintPosition::Before,
pad_left: false,
pad_right: false,
+ resolve_parent: None,
}
}
- pub fn needs_resolve(&self) -> bool {
- self.text_edit.is_some() || self.label.needs_resolve()
+ pub fn needs_resolve(&self) -> Option<TextRange> {
+ self.resolve_parent.filter(|_| self.text_edit.is_some() || self.label.needs_resolve())
}
}
@@ -434,190 +625,6 @@ fn label_of_ty(
Some(r)
}
-fn ty_to_text_edit(
- sema: &Semantics<'_, RootDatabase>,
- node_for_hint: &SyntaxNode,
- ty: &hir::Type,
- offset_to_insert: TextSize,
- prefix: String,
-) -> Option<TextEdit> {
- let scope = sema.scope(node_for_hint)?;
- // FIXME: Limit the length and bail out on excess somehow?
- let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
-
- let mut builder = TextEdit::builder();
- builder.insert(offset_to_insert, prefix);
- builder.insert(offset_to_insert, rendered);
- Some(builder.finish())
-}
-
-// Feature: Inlay Hints
-//
-// rust-analyzer shows additional information inline with the source code.
-// Editors usually render this using read-only virtual text snippets interspersed with code.
-//
-// rust-analyzer by default shows hints for
-//
-// * types of local variables
-// * names of function arguments
-// * names of const generic parameters
-// * types of chained expressions
-//
-// Optionally, one can enable additional hints for
-//
-// * return types of closure expressions
-// * elided lifetimes
-// * compiler inserted reborrows
-// * names of generic type and lifetime parameters
-//
-// Note: inlay hints for function argument names are heuristically omitted to reduce noise and will not appear if
-// any of the
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L92-L99[following criteria]
-// are met:
-//
-// * the parameter name is a suffix of the function's name
-// * the argument is a qualified constructing or call expression where the qualifier is an ADT
-// * exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
-// of argument with _ splitting it off
-// * the parameter name starts with `ra_fixture`
-// * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L200[well known name]
-// in a unary function
-// * the parameter name is a
-// link:https://github.com/rust-lang/rust-analyzer/blob/6b8b8ff4c56118ddee6c531cde06add1aad4a6af/crates/ide/src/inlay_hints/param_name.rs#L201[single character]
-// in a unary function
-//
-// image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
-pub(crate) fn inlay_hints(
- db: &RootDatabase,
- file_id: FileId,
- range_limit: Option<TextRange>,
- config: &InlayHintsConfig,
-) -> Vec<InlayHint> {
- let _p = tracing::info_span!("inlay_hints").entered();
- let sema = Semantics::new(db);
- let file_id = sema
- .attach_first_edition(file_id)
- .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
- let file = sema.parse(file_id);
- let file = file.syntax();
-
- let mut acc = Vec::new();
-
- if let Some(scope) = sema.scope(file) {
- let famous_defs = FamousDefs(&sema, scope.krate());
-
- let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
- match range_limit {
- Some(range) => match file.covering_element(range) {
- NodeOrToken::Token(_) => return acc,
- NodeOrToken::Node(n) => n
- .descendants()
- .filter(|descendant| range.intersect(descendant.text_range()).is_some())
- .for_each(hints),
- },
- None => file.descendants().for_each(hints),
- };
- }
-
- acc
-}
-
-pub(crate) fn inlay_hints_resolve(
- db: &RootDatabase,
- file_id: FileId,
- position: TextSize,
- hash: u64,
- config: &InlayHintsConfig,
- hasher: impl Fn(&InlayHint) -> u64,
-) -> Option<InlayHint> {
- let _p = tracing::info_span!("inlay_hints_resolve").entered();
- let sema = Semantics::new(db);
- let file_id = sema
- .attach_first_edition(file_id)
- .unwrap_or_else(|| EditionedFileId::current_edition(file_id));
- let file = sema.parse(file_id);
- let file = file.syntax();
-
- let scope = sema.scope(file)?;
- let famous_defs = FamousDefs(&sema, scope.krate());
- let mut acc = Vec::new();
-
- let hints = |node| hints(&mut acc, &famous_defs, config, file_id, node);
- let token = file.token_at_offset(position).left_biased()?;
- if let Some(parent_block) = token.parent_ancestors().find_map(ast::BlockExpr::cast) {
- parent_block.syntax().descendants().for_each(hints)
- } else if let Some(parent_item) = token.parent_ancestors().find_map(ast::Item::cast) {
- parent_item.syntax().descendants().for_each(hints)
- } else {
- return None;
- }
-
- acc.into_iter().find(|hint| hasher(hint) == hash)
-}
-
-fn hints(
- hints: &mut Vec<InlayHint>,
- famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
- config: &InlayHintsConfig,
- file_id: EditionedFileId,
- node: SyntaxNode,
-) {
- 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);
- }
- match_ast! {
- match node {
- ast::Expr(expr) => {
- chaining::hints(hints, famous_defs, config, file_id, &expr);
- adjustment::hints(hints, sema, config, file_id, &expr);
- match expr {
- 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))
- }
- ast::Expr::ClosureExpr(it) => {
- closure_captures::hints(hints, famous_defs, config, file_id, it.clone());
- closure_ret::hints(hints, famous_defs, config, file_id, it)
- },
- ast::Expr::RangeExpr(it) => range_exclusive::hints(hints, config, it),
- _ => None,
- }
- },
- ast::Pat(it) => {
- binding_mode::hints(hints, sema, config, &it);
- match it {
- ast::Pat::IdentPat(it) => {
- bind_pat::hints(hints, famous_defs, config, file_id, &it);
- }
- ast::Pat::RangePat(it) => {
- range_exclusive::hints(hints, config, it);
- }
- _ => {}
- }
- Some(())
- },
- ast::Item(it) => match it {
- // FIXME: record impl lifetimes so they aren't being reused in assoc item lifetime inlay hints
- ast::Item::Impl(_) => None,
- ast::Item::Fn(it) => {
- implicit_drop::hints(hints, sema, config, file_id, &it);
- fn_lifetime_fn::hints(hints, config, it)
- },
- // static type elisions
- 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::Enum(it) => discriminant::enum_hints(hints, famous_defs, config, file_id, it),
- _ => None,
- },
- // FIXME: fn-ptr type, dyn fn type, and trait object type elisions
- ast::Type(_) => None,
- _ => None,
- }
- };
-}
-
/// Checks if the type is an Iterator from std::iter and returns the iterator trait and the item type of the concrete iterator.
fn hint_iterator(
sema: &Semantics<'_, RootDatabase>,
@@ -653,6 +660,23 @@ fn hint_iterator(
None
}
+fn ty_to_text_edit(
+ sema: &Semantics<'_, RootDatabase>,
+ node_for_hint: &SyntaxNode,
+ ty: &hir::Type,
+ offset_to_insert: TextSize,
+ prefix: String,
+) -> Option<TextEdit> {
+ let scope = sema.scope(node_for_hint)?;
+ // FIXME: Limit the length and bail out on excess somehow?
+ let rendered = ty.display_source_code(scope.db, scope.module().into(), false).ok()?;
+
+ let mut builder = TextEdit::builder();
+ builder.insert(offset_to_insert, prefix);
+ builder.insert(offset_to_insert, rendered);
+ Some(builder.finish())
+}
+
fn closure_has_block_body(closure: &ast::ClosureExpr) -> bool {
matches!(closure.body(), Some(ast::Expr::BlockExpr(_)))
}
diff --git a/crates/ide/src/inlay_hints/adjustment.rs b/crates/ide/src/inlay_hints/adjustment.rs
index 31c1a991d5..dc390f8f67 100644
--- a/crates/ide/src/inlay_hints/adjustment.rs
+++ b/crates/ide/src/inlay_hints/adjustment.rs
@@ -6,9 +6,8 @@
use either::Either;
use hir::{
Adjust, Adjustment, AutoBorrow, HirDisplay, Mutability, OverloadedDeref, PointerCast, Safety,
- Semantics,
};
-use ide_db::RootDatabase;
+use ide_db::famous_defs::FamousDefs;
use span::EditionedFileId;
use stdx::never;
@@ -24,7 +23,7 @@ use crate::{
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
- sema: &Semantics<'_, RootDatabase>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
expr: &ast::Expr,
@@ -156,6 +155,7 @@ pub(super) fn hints(
kind: InlayKind::Adjustment,
label,
text_edit: None,
+ resolve_parent: Some(expr.syntax().text_range()),
});
}
if !postfix && needs_inner_parens {
diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs
index 82b0a6ffcf..7a808fb4a9 100644
--- a/crates/ide/src/inlay_hints/bind_pat.rs
+++ b/crates/ide/src/inlay_hints/bind_pat.rs
@@ -110,6 +110,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: !render_colons,
pad_right: false,
+ resolve_parent: Some(pat.syntax().text_range()),
});
Some(())
diff --git a/crates/ide/src/inlay_hints/binding_mode.rs b/crates/ide/src/inlay_hints/binding_mode.rs
index f27390ee89..d1c0677863 100644
--- a/crates/ide/src/inlay_hints/binding_mode.rs
+++ b/crates/ide/src/inlay_hints/binding_mode.rs
@@ -2,17 +2,19 @@
//! ```no_run
//! let /* & */ (/* ref */ x,) = &(0,);
//! ```
-use hir::{Mutability, Semantics};
-use ide_db::RootDatabase;
+use hir::Mutability;
+use ide_db::famous_defs::FamousDefs;
+use span::EditionedFileId;
use syntax::ast::{self, AstNode};
use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
- sema: &Semantics<'_, RootDatabase>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
pat: &ast::Pat,
) -> Option<()> {
if !config.binding_mode_hints {
@@ -57,6 +59,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: mut_reference,
+ resolve_parent: Some(pat.syntax().text_range()),
});
});
match pat {
@@ -75,6 +78,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: true,
+ resolve_parent: Some(pat.syntax().text_range()),
});
}
ast::Pat::OrPat(pat) if !pattern_adjustments.is_empty() && outer_paren_pat.is_none() => {
diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs
index 35f4d46e18..df34e4aa24 100644
--- a/crates/ide/src/inlay_hints/chaining.rs
+++ b/crates/ide/src/inlay_hints/chaining.rs
@@ -67,6 +67,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: true,
pad_right: false,
+ resolve_parent: Some(expr.syntax().text_range()),
});
}
}
diff --git a/crates/ide/src/inlay_hints/closing_brace.rs b/crates/ide/src/inlay_hints/closing_brace.rs
index d78fd64bdf..8af5bd5661 100644
--- a/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/crates/ide/src/inlay_hints/closing_brace.rs
@@ -18,12 +18,13 @@ pub(super) fn hints(
sema: &Semantics<'_, RootDatabase>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
- mut node: SyntaxNode,
+ original_node: SyntaxNode,
) -> Option<()> {
let min_lines = config.closing_brace_hints_min_lines?;
let name = |it: ast::Name| it.syntax().text_range();
+ let mut node = original_node.clone();
let mut closing_token;
let (label, name_range) = if let Some(item_list) = ast::AssocItemList::cast(node.clone()) {
closing_token = item_list.r_curly_token()?;
@@ -145,6 +146,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: true,
pad_right: false,
+ resolve_parent: Some(original_node.text_range()),
});
None
diff --git a/crates/ide/src/inlay_hints/closure_captures.rs b/crates/ide/src/inlay_hints/closure_captures.rs
index e87e10d850..adf7cbc365 100644
--- a/crates/ide/src/inlay_hints/closure_captures.rs
+++ b/crates/ide/src/inlay_hints/closure_captures.rs
@@ -40,6 +40,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: Some(closure.syntax().text_range()),
});
range
}
@@ -52,6 +53,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: None,
});
let last = captures.len() - 1;
for (idx, capture) in captures.into_iter().enumerate() {
@@ -85,6 +87,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: Some(closure.syntax().text_range()),
});
if idx != last {
@@ -96,6 +99,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: None,
});
}
}
@@ -107,6 +111,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
+ resolve_parent: None,
});
Some(())
diff --git a/crates/ide/src/inlay_hints/closure_ret.rs b/crates/ide/src/inlay_hints/closure_ret.rs
index 325c204069..6827540fa8 100644
--- a/crates/ide/src/inlay_hints/closure_ret.rs
+++ b/crates/ide/src/inlay_hints/closure_ret.rs
@@ -72,6 +72,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: Some(closure.syntax().text_range()),
});
Some(())
}
diff --git a/crates/ide/src/inlay_hints/discriminant.rs b/crates/ide/src/inlay_hints/discriminant.rs
index eca0ebe629..35b6287832 100644
--- a/crates/ide/src/inlay_hints/discriminant.rs
+++ b/crates/ide/src/inlay_hints/discriminant.rs
@@ -35,7 +35,7 @@ pub(super) fn enum_hints(
return None;
}
for variant in enum_.variant_list()?.variants() {
- variant_hints(acc, sema, &variant);
+ variant_hints(acc, sema, &enum_, &variant);
}
Some(())
}
@@ -43,6 +43,7 @@ pub(super) fn enum_hints(
fn variant_hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<'_, RootDatabase>,
+ enum_: &ast::Enum,
variant: &ast::Variant,
) -> Option<()> {
if variant.expr().is_some() {
@@ -90,6 +91,7 @@ fn variant_hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: Some(enum_.syntax().text_range()),
});
Some(())
diff --git a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
index d3666754e2..4d35e71a06 100644
--- a/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
+++ b/crates/ide/src/inlay_hints/fn_lifetime_fn.rs
@@ -2,8 +2,9 @@
//! ```no_run
//! fn example/* <'0> */(a: &/* '0 */()) {}
//! ```
-use ide_db::{syntax_helpers::node_ext::walk_ty, FxHashMap};
+use ide_db::{famous_defs::FamousDefs, syntax_helpers::node_ext::walk_ty, FxHashMap};
use itertools::Itertools;
+use span::EditionedFileId;
use syntax::{
ast::{self, AstNode, HasGenericParams, HasName},
SyntaxToken,
@@ -14,7 +15,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
+ FamousDefs(_, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
func: ast::Fn,
) -> Option<()> {
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
@@ -29,6 +32,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
+ resolve_parent: None,
};
let param_list = func.param_list()?;
@@ -195,6 +199,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
+ resolve_parent: None,
});
}
(None, allocated_lifetimes) => acc.push(InlayHint {
@@ -205,6 +210,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: false,
+ resolve_parent: None,
}),
}
Some(())
diff --git a/crates/ide/src/inlay_hints/generic_param.rs b/crates/ide/src/inlay_hints/generic_param.rs
index b60a80a8ac..ed7ebc3b1e 100644
--- a/crates/ide/src/inlay_hints/generic_param.rs
+++ b/crates/ide/src/inlay_hints/generic_param.rs
@@ -92,6 +92,7 @@ pub(crate) fn hints(
kind: InlayKind::GenericParameter,
label,
text_edit: None,
+ resolve_parent: Some(node.syntax().text_range()),
})
});
diff --git a/crates/ide/src/inlay_hints/implicit_drop.rs b/crates/ide/src/inlay_hints/implicit_drop.rs
index b4695a2b35..dd4b3efeec 100644
--- a/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -8,9 +8,9 @@
use hir::{
db::{DefDatabase as _, HirDatabase as _},
mir::{MirSpan, TerminatorKind},
- ChalkTyInterner, DefWithBody, Semantics,
+ ChalkTyInterner, DefWithBody,
};
-use ide_db::{FileRange, RootDatabase};
+use ide_db::{famous_defs::FamousDefs, FileRange};
use span::EditionedFileId;
use syntax::{
@@ -22,16 +22,16 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
- sema: &Semantics<'_, RootDatabase>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
file_id: EditionedFileId,
- def: &ast::Fn,
+ node: &ast::Fn,
) -> Option<()> {
if !config.implicit_drop_hints {
return None;
}
- let def = sema.to_def(def)?;
+ let def = sema.to_def(node)?;
let def: DefWithBody = def.into();
let (hir, source_map) = sema.db.body_with_source_map(def.into());
@@ -121,6 +121,7 @@ pub(super) fn hints(
kind: InlayKind::Drop,
label,
text_edit: None,
+ resolve_parent: Some(node.syntax().text_range()),
})
}
}
diff --git a/crates/ide/src/inlay_hints/implicit_static.rs b/crates/ide/src/inlay_hints/implicit_static.rs
index 42223ddf58..8d422478cb 100644
--- a/crates/ide/src/inlay_hints/implicit_static.rs
+++ b/crates/ide/src/inlay_hints/implicit_static.rs
@@ -3,6 +3,8 @@
//! static S: &/* 'static */str = "";
//! ```
use either::Either;
+use ide_db::famous_defs::FamousDefs;
+use span::EditionedFileId;
use syntax::{
ast::{self, AstNode},
SyntaxKind,
@@ -12,7 +14,9 @@ use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, LifetimeE
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
+ FamousDefs(_sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
statik_or_const: Either<ast::Static, ast::Const>,
) -> Option<()> {
if config.lifetime_elision_hints != LifetimeElisionHints::Always {
@@ -38,6 +42,7 @@ pub(super) fn hints(
position: InlayHintPosition::After,
pad_left: false,
pad_right: true,
+ resolve_parent: None,
});
}
}
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 0f3142ef3f..28b0fa6dd4 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -7,8 +7,9 @@ use std::fmt::Display;
use either::Either;
use hir::{Callable, Semantics};
-use ide_db::RootDatabase;
+use ide_db::{famous_defs::FamousDefs, RootDatabase};
+use span::EditionedFileId;
use stdx::to_lower_snake_case;
use syntax::{
ast::{self, AstNode, HasArgList, HasName, UnaryOp},
@@ -19,8 +20,9 @@ use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, Inla
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
- sema: &Semantics<'_, RootDatabase>,
+ FamousDefs(sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
expr: ast::Expr,
) -> Option<()> {
if !config.parameter_hints {
@@ -60,6 +62,7 @@ pub(super) fn hints(
position: InlayHintPosition::Before,
pad_left: false,
pad_right: true,
+ resolve_parent: Some(expr.syntax().text_range()),
}
});
diff --git a/crates/ide/src/inlay_hints/range_exclusive.rs b/crates/ide/src/inlay_hints/range_exclusive.rs
index bfb9283885..de9b0e98a4 100644
--- a/crates/ide/src/inlay_hints/range_exclusive.rs
+++ b/crates/ide/src/inlay_hints/range_exclusive.rs
@@ -3,13 +3,17 @@
//! for i in 0../* < */10 {}
//! if let ../* < */100 = 50 {}
//! ```
+use ide_db::famous_defs::FamousDefs;
+use span::EditionedFileId;
use syntax::{ast, SyntaxToken, T};
use crate::{InlayHint, InlayHintsConfig};
pub(super) fn hints(
acc: &mut Vec<InlayHint>,
+ FamousDefs(_sema, _): &FamousDefs<'_, '_>,
config: &InlayHintsConfig,
+ _file_id: EditionedFileId,
range: impl ast::RangeItem,
) -> Option<()> {
(config.range_exclusive_hints && range.end().is_some())
@@ -30,6 +34,7 @@ fn inlay_hint(token: SyntaxToken) -> InlayHint {
kind: crate::InlayKind::RangeExclusive,
label: crate::InlayHintLabel::from("<"),
text_edit: None,
+ resolve_parent: None,
}
}
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index ba0aaae19c..cdadfeea4b 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -439,12 +439,12 @@ impl Analysis {
&self,
config: &InlayHintsConfig,
file_id: FileId,
- position: TextSize,
+ resolve_range: TextRange,
hash: u64,
hasher: impl Fn(&InlayHint) -> u64 + Send + UnwindSafe,
) -> Cancellable<Option<InlayHint>> {
self.with_db(|db| {
- inlay_hints::inlay_hints_resolve(db, file_id, position, hash, config, hasher)
+ inlay_hints::inlay_hints_resolve(db, file_id, resolve_range, hash, config, hasher)
})
}
diff --git a/crates/rust-analyzer/src/handlers/request.rs b/crates/rust-analyzer/src/handlers/request.rs
index 1ad5ff0c8c..50df9dc876 100644
--- a/crates/rust-analyzer/src/handlers/request.rs
+++ b/crates/rust-analyzer/src/handlers/request.rs
@@ -1602,14 +1602,14 @@ pub(crate) fn handle_inlay_hints_resolve(
anyhow::ensure!(snap.file_exists(file_id), "Invalid LSP resolve data");
let line_index = snap.file_line_index(file_id)?;
- let hint_position = from_proto::offset(&line_index, original_hint.position)?;
+ let range = from_proto::text_range(&line_index, resolve_data.resolve_range)?;
let mut forced_resolve_inlay_hints_config = snap.config.inlay_hints();
forced_resolve_inlay_hints_config.fields_to_resolve = InlayFieldsToResolve::empty();
let resolve_hints = snap.analysis.inlay_hints_resolve(
&forced_resolve_inlay_hints_config,
file_id,
- hint_position,
+ range,
hash,
|hint| {
std::hash::BuildHasher::hash_one(
diff --git a/crates/rust-analyzer/src/lsp/ext.rs b/crates/rust-analyzer/src/lsp/ext.rs
index 8d1a686dc4..ed6f16a173 100644
--- a/crates/rust-analyzer/src/lsp/ext.rs
+++ b/crates/rust-analyzer/src/lsp/ext.rs
@@ -819,6 +819,7 @@ pub struct InlayHintResolveData {
pub file_id: u32,
// This is a string instead of a u64 as javascript can't represent u64 fully
pub hash: String,
+ pub resolve_range: lsp_types::Range,
pub version: Option<i32>,
}
diff --git a/crates/rust-analyzer/src/lsp/to_proto.rs b/crates/rust-analyzer/src/lsp/to_proto.rs
index eb6bc2a9ce..7eed9ce2f3 100644
--- a/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -452,10 +452,13 @@ pub(crate) fn inlay_hint(
file_id: FileId,
mut inlay_hint: InlayHint,
) -> Cancellable<lsp_types::InlayHint> {
- let resolve_hash = inlay_hint.needs_resolve().then(|| {
- std::hash::BuildHasher::hash_one(
- &std::hash::BuildHasherDefault::<FxHasher>::default(),
- &inlay_hint,
+ let resolve_range_and_hash = inlay_hint.needs_resolve().map(|range| {
+ (
+ range,
+ std::hash::BuildHasher::hash_one(
+ &std::hash::BuildHasherDefault::<FxHasher>::default(),
+ &inlay_hint,
+ ),
)
});
@@ -465,7 +468,7 @@ pub(crate) fn inlay_hint(
.visual_studio_code_version()
// https://github.com/microsoft/vscode/issues/193124
.map_or(true, |version| VersionReq::parse(">=1.86.0").unwrap().matches(version))
- && resolve_hash.is_some()
+ && resolve_range_and_hash.is_some()
&& fields_to_resolve.resolve_text_edits
{
something_to_resolve |= inlay_hint.text_edit.is_some();
@@ -477,16 +480,17 @@ pub(crate) fn inlay_hint(
snap,
fields_to_resolve,
&mut something_to_resolve,
- resolve_hash.is_some(),
+ resolve_range_and_hash.is_some(),
inlay_hint.label,
)?;
- let data = match resolve_hash {
- Some(hash) if something_to_resolve => Some(
+ let data = match resolve_range_and_hash {
+ Some((resolve_range, hash)) if something_to_resolve => Some(
to_value(lsp_ext::InlayHintResolveData {
file_id: file_id.index(),
hash: hash.to_string(),
version: snap.file_version(file_id),
+ resolve_range: range(line_index, resolve_range),
})
.unwrap(),
),
diff --git a/docs/dev/lsp-extensions.md b/docs/dev/lsp-extensions.md
index 4786bd54d5..68afacf2db 100644
--- a/docs/dev/lsp-extensions.md
+++ b/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
<!---
-lsp/ext.rs hash: 3429c08745984b3d
+lsp/ext.rs hash: c6e83d3d08d993de
If you need to change the above hash to make the test pass, please check if you
need to adjust this doc as well and ping this issue: