From 38b8e8c6b2f4de66113c354b6eb4c56f66b11912 Mon Sep 17 00:00:00 2001 From: Asuka Minato Date: Sun, 21 Dec 2025 05:42:12 +0900 Subject: [PATCH] impl fix test clippy --- crates/ide/src/inlay_hints.rs | 13 ++- crates/ide/src/inlay_hints/bind_pat.rs | 98 +++++++++++++++- crates/ide/src/inlay_hints/chaining.rs | 107 ++++++++++++++++-- crates/ide/src/lib.rs | 2 +- crates/ide/src/static_index.rs | 3 +- .../rust-analyzer/src/cli/analysis_stats.rs | 1 + crates/rust-analyzer/src/config.rs | 22 ++++ docs/book/src/configuration_generated.md | 7 ++ editors/code/package.json | 18 +++ 9 files changed, 252 insertions(+), 19 deletions(-) diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs index f57f2883b1c3..69b3db644633 100644 --- a/crates/ide/src/inlay_hints.rs +++ b/crates/ide/src/inlay_hints.rs @@ -302,6 +302,7 @@ fn hints( pub struct InlayHintsConfig<'a> { pub render_colons: bool, pub type_hints: bool, + pub type_hints_placement: TypeHintsPlacement, pub sized_bound: bool, pub discriminant_hints: DiscriminantHints, pub parameter_hints: bool, @@ -331,6 +332,12 @@ pub struct InlayHintsConfig<'a> { pub minicore: MiniCore<'a>, } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum TypeHintsPlacement { + Inline, + EndOfLine, +} + impl InlayHintsConfig<'_> { fn lazy_text_edit(&self, finish: impl FnOnce() -> TextEdit) -> LazyProperty { if self.fields_to_resolve.resolve_text_edits { @@ -876,12 +883,15 @@ mod tests { use crate::inlay_hints::{AdjustmentHints, AdjustmentHintsMode}; use crate::{LifetimeElisionHints, fixture, inlay_hints::InlayHintsConfig}; - use super::{ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve}; + use super::{ + ClosureReturnTypeHints, GenericParameterHints, InlayFieldsToResolve, TypeHintsPlacement, + }; pub(super) const DISABLED_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { discriminant_hints: DiscriminantHints::Never, render_colons: false, type_hints: false, + type_hints_placement: TypeHintsPlacement::Inline, parameter_hints: false, parameter_hints_for_missing_arguments: false, sized_bound: false, @@ -915,6 +925,7 @@ mod tests { }; pub(super) const TEST_CONFIG: InlayHintsConfig<'_> = InlayHintsConfig { type_hints: true, + type_hints_placement: TypeHintsPlacement::Inline, parameter_hints: true, chaining_hints: true, closure_return_type_hints: ClosureReturnTypeHints::WithBlock, diff --git a/crates/ide/src/inlay_hints/bind_pat.rs b/crates/ide/src/inlay_hints/bind_pat.rs index de207c7821da..5e16be332e8e 100644 --- a/crates/ide/src/inlay_hints/bind_pat.rs +++ b/crates/ide/src/inlay_hints/bind_pat.rs @@ -8,10 +8,12 @@ use ide_db::{RootDatabase, famous_defs::FamousDefs}; use itertools::Itertools; use syntax::{ + TextRange, ast::{self, AstNode, HasGenericArgs, HasName}, match_ast, }; +use super::TypeHintsPlacement; use crate::{ InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind, inlay_hints::{closure_has_block_body, label_of_ty, ty_to_text_edit}, @@ -29,6 +31,7 @@ pub(super) fn hints( } let parent = pat.syntax().parent()?; + let mut enclosing_let_stmt = None; let type_ascriptable = match_ast! { match parent { ast::Param(it) => { @@ -41,6 +44,7 @@ pub(super) fn hints( Some(it.colon_token()) }, ast::LetStmt(it) => { + enclosing_let_stmt = Some(it.clone()); if config.hide_closure_initialization_hints && let Some(ast::Expr::ClosureExpr(closure)) = it.initializer() && closure_has_block_body(&closure) { @@ -101,16 +105,26 @@ pub(super) fn hints( Some(name) => name.syntax().text_range(), None => pat.syntax().text_range(), }; + let mut range = match type_ascriptable { + Some(Some(t)) => text_range.cover(t.text_range()), + _ => text_range, + }; + + let mut pad_left = !render_colons; + if matches!(config.type_hints_placement, TypeHintsPlacement::EndOfLine) + && let Some(let_stmt) = enclosing_let_stmt + { + let stmt_range = let_stmt.syntax().text_range(); + range = TextRange::new(range.start(), stmt_range.end()); + pad_left = true; + } acc.push(InlayHint { - range: match type_ascriptable { - Some(Some(t)) => text_range.cover(t.text_range()), - _ => text_range, - }, + range, kind: InlayKind::Type, label, text_edit, position: InlayHintPosition::After, - pad_left: !render_colons, + pad_left, pad_right: false, resolve_parent: Some(pat.syntax().text_range()), }); @@ -182,8 +196,10 @@ mod tests { use crate::{ClosureReturnTypeHints, fixture, inlay_hints::InlayHintsConfig}; + use super::TypeHintsPlacement; use crate::inlay_hints::tests::{ - DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_no_edit, check_with_config, + DISABLED_CONFIG, TEST_CONFIG, check, check_edit, check_expect, check_no_edit, + check_with_config, }; #[track_caller] @@ -203,6 +219,76 @@ fn main() { ); } + #[test] + fn type_hints_end_of_line_placement() { + let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }; + config.type_hints_placement = TypeHintsPlacement::EndOfLine; + check_expect( + config, + r#" +fn main() { + let foo = 92_i32; +} + "#, + expect![[r#" + [ + ( + 20..33, + [ + "i32", + ], + ), + ] + "#]], + ); + } + + #[test] + fn type_hints_end_of_line_placement_chain_expr() { + let mut config = InlayHintsConfig { type_hints: true, ..DISABLED_CONFIG }; + config.type_hints_placement = TypeHintsPlacement::EndOfLine; + check_expect( + config, + r#" +fn main() { + struct Builder; + impl Builder { + fn iter(self) -> Builder { Builder } + fn map(self) -> Builder { Builder } + } + fn make() -> Builder { Builder } + + let foo = make() + .iter() + .map(); +} +"#, + expect![[r#" + [ + ( + 192..236, + [ + InlayHintLabelPart { + text: "Builder", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 23..30, + }, + ), + ), + tooltip: "", + }, + ], + ), + ] + "#]], + ); + } + #[test] fn type_hints_bindings_after_at() { check_types( diff --git a/crates/ide/src/inlay_hints/chaining.rs b/crates/ide/src/inlay_hints/chaining.rs index cf3149c9461b..4b06f83971b2 100644 --- a/crates/ide/src/inlay_hints/chaining.rs +++ b/crates/ide/src/inlay_hints/chaining.rs @@ -2,13 +2,13 @@ use hir::DisplayTarget; use ide_db::famous_defs::FamousDefs; use syntax::{ - Direction, NodeOrToken, SyntaxKind, T, + Direction, NodeOrToken, SyntaxKind, T, TextRange, ast::{self, AstNode}, }; use crate::{InlayHint, InlayHintPosition, InlayHintsConfig, InlayKind}; -use super::label_of_ty; +use super::{TypeHintsPlacement, label_of_ty}; pub(super) fn hints( acc: &mut Vec, @@ -40,13 +40,14 @@ pub(super) fn hints( // Chaining can be defined as an expression whose next sibling tokens are newline and dot // Ignoring extra whitespace and comments - let next = tokens.next()?.kind(); - if next == SyntaxKind::WHITESPACE { - let mut next_next = tokens.next()?.kind(); - while next_next == SyntaxKind::WHITESPACE { - next_next = tokens.next()?.kind(); + let next_token = tokens.next()?; + if next_token.kind() == SyntaxKind::WHITESPACE { + let newline_token = next_token; + let mut next_next = tokens.next()?; + while next_next.kind() == SyntaxKind::WHITESPACE { + next_next = tokens.next()?; } - if next_next == T![.] { + if next_next.kind() == T![.] { let ty = sema.type_of_expr(desc_expr)?.original; if ty.is_unknown() { return None; @@ -58,8 +59,18 @@ pub(super) fn hints( return None; } let label = label_of_ty(famous_defs, config, &ty, display_target)?; + let range = { + let mut range = expr.syntax().text_range(); + if config.type_hints_placement == TypeHintsPlacement::EndOfLine { + range = TextRange::new( + range.start(), + newline_token.text_range().start().max(range.end()), + ); + } + range + }; acc.push(InlayHint { - range: expr.syntax().text_range(), + range, kind: InlayKind::Chaining, label, text_edit: None, @@ -79,7 +90,7 @@ mod tests { use ide_db::text_edit::{TextRange, TextSize}; use crate::{ - InlayHintsConfig, fixture, + InlayHintsConfig, TypeHintsPlacement, fixture, inlay_hints::{ LazyProperty, tests::{DISABLED_CONFIG, TEST_CONFIG, check_expect, check_with_config}, @@ -686,4 +697,80 @@ fn main() { "#]], ); } + + #[test] + fn chaining_hints_end_of_line_placement() { + check_expect( + InlayHintsConfig { + chaining_hints: true, + type_hints_placement: TypeHintsPlacement::EndOfLine, + ..DISABLED_CONFIG + }, + r#" +fn main() { + let baz = make() + .into_bar() + .into_baz(); +} + +struct Foo; +struct Bar; +struct Baz; + +impl Foo { + fn into_bar(self) -> Bar { Bar } +} + +impl Bar { + fn into_baz(self) -> Baz { Baz } +} + +fn make() -> Foo { + Foo +} +"#, + expect![[r#" + [ + ( + 26..52, + [ + InlayHintLabelPart { + text: "Bar", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 96..99, + }, + ), + ), + tooltip: "", + }, + ], + ), + ( + 26..32, + [ + InlayHintLabelPart { + text: "Foo", + linked_location: Some( + Computed( + FileRangeWrapper { + file_id: FileId( + 0, + ), + range: 84..87, + }, + ), + ), + tooltip: "", + }, + ], + ), + ] + "#]], + ); + } } diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs index 5e4d930393af..da2a0aa1f266 100644 --- a/crates/ide/src/lib.rs +++ b/crates/ide/src/lib.rs @@ -96,7 +96,7 @@ pub use crate::{ AdjustmentHints, AdjustmentHintsMode, ClosureReturnTypeHints, DiscriminantHints, GenericParameterHints, InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind, InlayTooltip, LazyProperty, - LifetimeElisionHints, + LifetimeElisionHints, TypeHintsPlacement, }, join_lines::JoinLinesConfig, markup::Markup, diff --git a/crates/ide/src/static_index.rs b/crates/ide/src/static_index.rs index aba6b64f977a..14af86f05030 100644 --- a/crates/ide/src/static_index.rs +++ b/crates/ide/src/static_index.rs @@ -16,7 +16,7 @@ use crate::navigation_target::UpmappingResult; use crate::{ Analysis, Fold, HoverConfig, HoverResult, InlayHint, InlayHintsConfig, TryToNav, hover::{SubstTyLen, hover_for_definition}, - inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve}, + inlay_hints::{AdjustmentHintsMode, InlayFieldsToResolve, TypeHintsPlacement}, moniker::{MonikerResult, SymbolInformationKind, def_to_kind, def_to_moniker}, parent_module::crates_for, }; @@ -167,6 +167,7 @@ impl StaticIndex<'_> { render_colons: true, discriminant_hints: crate::DiscriminantHints::Fieldless, type_hints: true, + type_hints_placement: TypeHintsPlacement::Inline, sized_bound: false, parameter_hints: true, parameter_hints_for_missing_arguments: false, diff --git a/crates/rust-analyzer/src/cli/analysis_stats.rs b/crates/rust-analyzer/src/cli/analysis_stats.rs index 76256b0a2253..75d6710b5b53 100644 --- a/crates/rust-analyzer/src/cli/analysis_stats.rs +++ b/crates/rust-analyzer/src/cli/analysis_stats.rs @@ -1204,6 +1204,7 @@ impl flags::AnalysisStats { &InlayHintsConfig { render_colons: false, type_hints: true, + type_hints_placement: ide::TypeHintsPlacement::Inline, sized_bound: false, discriminant_hints: ide::DiscriminantHints::Always, parameter_hints: true, diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs index 2371f7a65649..010cbd9e4c19 100644 --- a/crates/rust-analyzer/src/config.rs +++ b/crates/rust-analyzer/src/config.rs @@ -320,6 +320,9 @@ config_data! { /// Hide inlay type hints for constructors. inlayHints_typeHints_hideNamedConstructor: bool = false, + /// Where to render type hints relative to their binding pattern. + inlayHints_typeHints_location: TypeHintsLocation = TypeHintsLocation::Inline, + /// Enable the experimental support for interpreting tests. interpret_tests: bool = false, @@ -1926,6 +1929,10 @@ impl Config { InlayHintsConfig { render_colons: self.inlayHints_renderColons().to_owned(), type_hints: self.inlayHints_typeHints_enable().to_owned(), + type_hints_placement: match self.inlayHints_typeHints_location() { + TypeHintsLocation::Inline => ide::TypeHintsPlacement::Inline, + TypeHintsLocation::EndOfLine => ide::TypeHintsPlacement::EndOfLine, + }, sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(), parameter_hints: self.inlayHints_parameterHints_enable().to_owned(), parameter_hints_for_missing_arguments: self @@ -2908,6 +2915,13 @@ enum ClosureStyle { Hide, } +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(rename_all = "snake_case")] +enum TypeHintsLocation { + Inline, + EndOfLine, +} + #[derive(Serialize, Deserialize, Debug, Clone)] #[serde(rename_all = "snake_case")] enum ReborrowHintsDef { @@ -3811,6 +3825,14 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json "`hide`: Shows `...` for every closure type", ], }, + "TypeHintsLocation" => set! { + "type": "string", + "enum": ["inline", "end_of_line"], + "enumDescriptions": [ + "Render type hints directly after the binding identifier.", + "Render type hints after the end of the containing `let` statement when possible.", + ], + }, "Option" => set! { "anyOf": [ { diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md index 6b7ef049645c..78281ec6359f 100644 --- a/docs/book/src/configuration_generated.md +++ b/docs/book/src/configuration_generated.md @@ -1150,6 +1150,13 @@ Default: `false` Hide inlay type hints for constructors. +## rust-analyzer.inlayHints.typeHints.location {#inlayHints.typeHints.location} + +Default: `"inline"` + +Where to render type hints relative to their binding pattern. + + ## rust-analyzer.interpret.tests {#interpret.tests} Default: `false` diff --git a/editors/code/package.json b/editors/code/package.json index d0410c70da67..d3d2ea8d1938 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -2512,6 +2512,24 @@ } } }, + { + "title": "Inlay Hints", + "properties": { + "rust-analyzer.inlayHints.typeHints.location": { + "markdownDescription": "Where to render type hints relative to their binding pattern.", + "default": "inline", + "type": "string", + "enum": [ + "inline", + "end_of_line" + ], + "enumDescriptions": [ + "Render type hints directly after the binding identifier.", + "Render type hints after the end of the containing `let` statement when possible." + ] + } + } + }, { "title": "Interpret", "properties": {