Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/ide/src/inlay_hints.rs2
-rw-r--r--crates/ide/src/inlay_hints/param_name.rs195
-rw-r--r--crates/ide/src/static_index.rs1
-rw-r--r--crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--crates/rust-analyzer/src/config.rs6
-rw-r--r--docs/book/src/configuration_generated.md7
-rw-r--r--editors/code/package.json10
7 files changed, 222 insertions, 0 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index 03674978d5..d05c7811b0 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/crates/ide/src/inlay_hints.rs
@@ -307,6 +307,7 @@ pub struct InlayHintsConfig<'a> {
pub sized_bound: bool,
pub discriminant_hints: DiscriminantHints,
pub parameter_hints: bool,
+ pub parameter_hints_for_missing_arguments: bool,
pub generic_parameter_hints: GenericParameterHints,
pub chaining_hints: bool,
pub adjustment_hints: AdjustmentHints,
@@ -886,6 +887,7 @@ mod tests {
render_colons: false,
type_hints: false,
parameter_hints: false,
+ parameter_hints_for_missing_arguments: false,
sized_bound: false,
generic_parameter_hints: GenericParameterHints {
type_hints: false,
diff --git a/crates/ide/src/inlay_hints/param_name.rs b/crates/ide/src/inlay_hints/param_name.rs
index 8d03487673..f1e62a5ab8 100644
--- a/crates/ide/src/inlay_hints/param_name.rs
+++ b/crates/ide/src/inlay_hints/param_name.rs
@@ -11,6 +11,7 @@ use hir::{EditionedFileId, Semantics};
use ide_db::{RootDatabase, famous_defs::FamousDefs};
use stdx::to_lower_snake_case;
+use syntax::T;
use syntax::ast::{self, AstNode, HasArgList, HasName, UnaryOp};
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
@@ -88,9 +89,75 @@ pub(super) fn hints(
});
acc.extend(hints);
+
+ // Show hint for the next expected (missing) argument if enabled
+ if config.parameter_hints_for_missing_arguments {
+ let provided_args_count = arg_list.args().count();
+ let params = callable.params();
+ let total_params = params.len();
+
+ if provided_args_count < total_params
+ && let Some(next_param) = params.get(provided_args_count)
+ && let Some(param_name) = next_param.name(sema.db)
+ {
+ // Apply heuristics to hide obvious parameter hints
+ if should_hide_missing_param_hint(unary_function, function_name, param_name.as_str()) {
+ return Some(());
+ }
+
+ // Determine the position for the hint
+ if let Some(hint_range) = missing_arg_hint_position(&arg_list) {
+ let colon = if config.render_colons { ":" } else { "" };
+ let label = InlayHintLabel::simple(
+ format!("{}{}", param_name.display(sema.db, krate.edition(sema.db)), colon),
+ None,
+ config.lazy_location_opt(|| {
+ let source = sema.source(next_param.clone())?;
+ 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(|frange| {
+ ide_db::FileRange {
+ file_id: frange.file_id.file_id(sema.db),
+ range: frange.range,
+ }
+ })
+ }),
+ );
+ acc.push(InlayHint {
+ range: hint_range,
+ kind: InlayKind::Parameter,
+ label,
+ text_edit: None,
+ position: InlayHintPosition::Before,
+ pad_left: true,
+ pad_right: false,
+ resolve_parent: Some(expr.syntax().text_range()),
+ });
+ }
+ }
+ }
+
Some(())
}
+/// Determines the position where the hint for a missing argument should be placed.
+/// Returns the range of the token where the hint should appear.
+fn missing_arg_hint_position(arg_list: &ast::ArgList) -> Option<syntax::TextRange> {
+ // Always place the hint on the closing paren, so it appears before `)`.
+ // This way `foo()` becomes `foo(a)` visually with the hint.
+ arg_list
+ .syntax()
+ .children_with_tokens()
+ .filter_map(|it| it.into_token())
+ .find(|t| t.kind() == T![')'])
+ .map(|t| t.text_range())
+}
+
fn get_callable<'db>(
sema: &Semantics<'db, RootDatabase>,
expr: &ast::Expr,
@@ -153,6 +220,37 @@ fn should_hide_param_name_hint(
is_argument_expr_similar_to_param_name(sema, argument, param_name)
}
+/// Determines whether to hide the parameter hint for a missing argument.
+/// This is a simplified version of `should_hide_param_name_hint` that doesn't
+/// require an actual argument expression.
+fn should_hide_missing_param_hint(
+ unary_function: bool,
+ function_name: Option<&str>,
+ param_name: &str,
+) -> bool {
+ let param_name = param_name.trim_matches('_');
+ if param_name.is_empty() {
+ return true;
+ }
+
+ if param_name.starts_with("ra_fixture") {
+ return true;
+ }
+
+ if unary_function {
+ if let Some(function_name) = function_name
+ && is_param_name_suffix_of_fn_name(param_name, function_name)
+ {
+ return true;
+ }
+ if is_obvious_param(param_name) {
+ return true;
+ }
+ }
+
+ false
+}
+
/// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
///
/// `fn strip_suffix(suffix)` will be hidden.
@@ -609,4 +707,101 @@ fn main() {
}"#,
);
}
+
+ #[track_caller]
+ fn check_missing_params(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+ check_with_config(
+ InlayHintsConfig {
+ parameter_hints: true,
+ parameter_hints_for_missing_arguments: true,
+ ..DISABLED_CONFIG
+ },
+ ra_fixture,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_empty_call() {
+ // When calling foo() with no args, show hint for first param on the closing paren
+ check_missing_params(
+ r#"
+fn foo(a: i32, b: i32) -> i32 { a + b }
+fn main() {
+ foo();
+ //^ a
+}"#,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_after_first_arg() {
+ // foo(1,) - show hint for 'a' on '1', and 'b' on the trailing comma
+ check_missing_params(
+ r#"
+fn foo(a: i32, b: i32) -> i32 { a + b }
+fn main() {
+ foo(1,);
+ //^ a
+ //^ b
+}"#,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_partial_args() {
+ // foo(1, 2,) - show hints for a, b on args, and c on trailing comma
+ check_missing_params(
+ r#"
+fn foo(a: i32, b: i32, c: i32) -> i32 { a + b + c }
+fn main() {
+ foo(1, 2,);
+ //^ a
+ //^ b
+ //^ c
+}"#,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_method_call() {
+ // S.foo(1,) - show hint for 'a' on '1', and 'b' on trailing comma
+ check_missing_params(
+ r#"
+struct S;
+impl S {
+ fn foo(&self, a: i32, b: i32) -> i32 { a + b }
+}
+fn main() {
+ S.foo(1,);
+ //^ a
+ //^ b
+}"#,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_no_hint_when_complete() {
+ // When all args provided, no missing hint - just regular param hints
+ check_missing_params(
+ r#"
+fn foo(a: i32, b: i32) -> i32 { a + b }
+fn main() {
+ foo(1, 2);
+ //^ a
+ //^ b
+}"#,
+ );
+ }
+
+ #[test]
+ fn missing_param_hint_respects_heuristics() {
+ // The hint should be hidden if it matches heuristics (e.g., single param unary fn with same name)
+ check_missing_params(
+ r#"
+fn foo(foo: i32) -> i32 { foo }
+fn main() {
+ foo();
+}"#,
+ );
+ }
}
diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs
index 9d3fd9e005..aba6b64f97 100644
--- a/crates/ide/src/static_index.rs
+++ b/crates/ide/src/static_index.rs
@@ -169,6 +169,7 @@ impl StaticIndex<'_> {
type_hints: true,
sized_bound: false,
parameter_hints: true,
+ parameter_hints_for_missing_arguments: false,
generic_parameter_hints: crate::GenericParameterHints {
type_hints: false,
lifetime_hints: false,
diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs
index f39ab1301f..1a6cd784cf 100644
--- a/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1205,6 +1205,7 @@ impl flags::AnalysisStats {
sized_bound: false,
discriminant_hints: ide::DiscriminantHints::Always,
parameter_hints: true,
+ parameter_hints_for_missing_arguments: false,
generic_parameter_hints: ide::GenericParameterHints {
type_hints: true,
lifetime_hints: true,
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 1a2ea97204..007725be74 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -280,6 +280,9 @@ config_data! {
/// Show function parameter name inlay hints at the call site.
inlayHints_parameterHints_enable: bool = true,
+ /// Show parameter name inlay hints for missing arguments at the call site.
+ inlayHints_parameterHints_missingArguments_enable: bool = false,
+
/// Show exclusive range inlay hints.
inlayHints_rangeExclusiveHints_enable: bool = false,
@@ -1916,6 +1919,9 @@ impl Config {
type_hints: self.inlayHints_typeHints_enable().to_owned(),
sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
+ parameter_hints_for_missing_arguments: self
+ .inlayHints_parameterHints_missingArguments_enable()
+ .to_owned(),
generic_parameter_hints: GenericParameterHints {
type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
lifetime_hints: self.inlayHints_genericParameterHints_lifetime_enable().to_owned(),
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index b36576b4bb..1f5c672233 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -1070,6 +1070,13 @@ Default: `true`
Show function parameter name inlay hints at the call site.
+## rust-analyzer.inlayHints.parameterHints.missingArguments.enable {#inlayHints.parameterHints.missingArguments.enable}
+
+Default: `false`
+
+Show parameter name inlay hints for missing arguments at the call site.
+
+
## rust-analyzer.inlayHints.rangeExclusiveHints.enable {#inlayHints.rangeExclusiveHints.enable}
Default: `false`
diff --git a/editors/code/package.json b/editors/code/package.json
index abe85d6c9d..98fe6a558b 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -2399,6 +2399,16 @@
{
"title": "Inlay Hints",
"properties": {
+ "rust-analyzer.inlayHints.parameterHints.missingArguments.enable": {
+ "markdownDescription": "Show parameter name inlay hints for missing arguments at the call site.",
+ "default": false,
+ "type": "boolean"
+ }
+ }
+ },
+ {
+ "title": "Inlay Hints",
+ "properties": {
"rust-analyzer.inlayHints.rangeExclusiveHints.enable": {
"markdownDescription": "Show exclusive range inlay hints.",
"default": false,