about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <lukastw97@gmail.com>2025-01-10 11:53:18 +0000
committerGitHub <noreply@github.com>2025-01-10 11:53:18 +0000
commit0a354a4025ebc708c23a174cc4f2d6eb53b4c42a (patch)
tree22a89d6df65584a00d4086c5911e20e1d86b214f
parent65a97e6475b00b3d343708b22602acfce27657e6 (diff)
parent17a6f31738dc2f7b3ffdd597880d35d1a55f9416 (diff)
downloadrust-0a354a4025ebc708c23a174cc4f2d6eb53b4c42a.tar.gz
rust-0a354a4025ebc708c23a174cc4f2d6eb53b4c42a.zip
Merge pull request #18903 from Veykril/push-mqmworppxuyw
Implement implicit sized bound inlay hints
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs152
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs11
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/static_index.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs3
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc5
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json10
10 files changed, 192 insertions, 12 deletions
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
index df56f8904b2..cd6f900ba15 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs
@@ -38,8 +38,6 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
 pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
     let macro_call = ctx.sema.to_def(&unexpanded)?;
-    let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
-    let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
     let target_crate_id = ctx.sema.file_to_module_def(ctx.file_id())?.krate().into();
     let text_range = unexpanded.syntax().text_range();
 
@@ -48,6 +46,8 @@ pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
         "Inline macro".to_owned(),
         text_range,
         |builder| {
+            let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
+            let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
             // Don't call `prettify_macro_expansion()` outside the actual assist action; it does some heavy rowan tree manipulation,
             // which can be very costly for big macros when it is done *even without the assist being invoked*.
             let expanded = prettify_macro_expansion(ctx.db(), expanded, &span_map, target_crate_id);
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index ba6e50abf65..9e3506d6f53 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -106,6 +106,10 @@ impl FamousDefs<'_, '_> {
         self.find_trait("core:marker:Copy")
     }
 
+    pub fn core_marker_Sized(&self) -> Option<Trait> {
+        self.find_trait("core:marker:Sized")
+    }
+
     pub fn core_future_Future(&self) -> Option<Trait> {
         self.find_trait("core:future:Future")
     }
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
index 31b1774bb71..1c08e0b5602 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -24,6 +24,7 @@ use crate::{navigation_target::TryToNav, FileId};
 mod adjustment;
 mod bind_pat;
 mod binding_mode;
+mod bounds;
 mod chaining;
 mod closing_brace;
 mod closure_captures;
@@ -264,6 +265,7 @@ fn hints(
                 ast::Type::PathType(path) => lifetime::fn_path_hints(hints, ctx, famous_defs, config, file_id, path),
                 _ => Some(()),
             },
+            ast::GenericParamList(it) => bounds::hints(hints, famous_defs, config, file_id, it),
             _ => Some(()),
         }
     };
@@ -273,6 +275,7 @@ fn hints(
 pub struct InlayHintsConfig {
     pub render_colons: bool,
     pub type_hints: bool,
+    pub sized_bound: bool,
     pub discriminant_hints: DiscriminantHints,
     pub parameter_hints: bool,
     pub generic_parameter_hints: GenericParameterHints,
@@ -760,6 +763,7 @@ mod tests {
         render_colons: false,
         type_hints: false,
         parameter_hints: false,
+        sized_bound: false,
         generic_parameter_hints: GenericParameterHints {
             type_hints: false,
             lifetime_hints: false,
@@ -814,6 +818,15 @@ mod tests {
         assert_eq!(expected, actual, "\nExpected:\n{expected:#?}\n\nActual:\n{actual:#?}");
     }
 
+    #[track_caller]
+    pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
+        let (analysis, file_id) = fixture::file(ra_fixture);
+        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
+        let filtered =
+            inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
+        expect.assert_debug_eq(&filtered)
+    }
+
     /// Computes inlay hints for the fixture, applies all the provided text edits and then runs
     /// expect test.
     #[track_caller]
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
new file mode 100644
index 00000000000..334f3ca6311
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/bounds.rs
@@ -0,0 +1,152 @@
+//! Implementation of trait bound hints.
+//!
+//! Currently this renders the implied `Sized` bound.
+use ide_db::{famous_defs::FamousDefs, FileRange};
+
+use span::EditionedFileId;
+use syntax::ast::{self, AstNode, HasTypeBounds};
+
+use crate::{
+    InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintPosition, InlayHintsConfig, InlayKind,
+    TryToNav,
+};
+
+pub(super) fn hints(
+    acc: &mut Vec<InlayHint>,
+    famous_defs @ FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    _file_id: EditionedFileId,
+    params: ast::GenericParamList,
+) -> Option<()> {
+    if !config.sized_bound {
+        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() }
+        });
+
+    for param in params.type_or_const_params() {
+        match param {
+            ast::TypeOrConstParam::Type(type_param) => {
+                let c = type_param.colon_token().map(|it| it.text_range());
+                let has_bounds =
+                    type_param.type_bound_list().is_some_and(|it| it.bounds().next().is_some());
+                acc.push(InlayHint {
+                    range: c.unwrap_or_else(|| type_param.syntax().text_range()),
+                    kind: InlayKind::Type,
+                    label: {
+                        let mut hint = InlayHintLabel::default();
+                        if c.is_none() {
+                            hint.parts.push(InlayHintLabelPart {
+                                text: ": ".to_owned(),
+                                linked_location: None,
+                                tooltip: None,
+                            });
+                        }
+                        hint.parts.push(InlayHintLabelPart {
+                            text: "Sized".to_owned(),
+                            linked_location,
+                            tooltip: None,
+                        });
+                        if has_bounds {
+                            hint.parts.push(InlayHintLabelPart {
+                                text: " +".to_owned(),
+                                linked_location: None,
+                                tooltip: None,
+                            });
+                        }
+                        hint
+                    },
+                    text_edit: None,
+                    position: InlayHintPosition::After,
+                    pad_left: c.is_some(),
+                    pad_right: has_bounds,
+                    resolve_parent: Some(params.syntax().text_range()),
+                });
+            }
+            ast::TypeOrConstParam::Const(_) => (),
+        }
+    }
+
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+    use expect_test::expect;
+
+    use crate::inlay_hints::InlayHintsConfig;
+
+    use crate::inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG};
+
+    #[track_caller]
+    fn check(ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[test]
+    fn smoke() {
+        check(
+            r#"
+fn foo<T>() {}
+    // ^ : Sized
+"#,
+        );
+    }
+
+    #[test]
+    fn with_colon() {
+        check(
+            r#"
+fn foo<T:>() {}
+     // ^ Sized
+"#,
+        );
+    }
+
+    #[test]
+    fn with_colon_and_bounds() {
+        check(
+            r#"
+fn foo<T: 'static>() {}
+     // ^ Sized +
+"#,
+        );
+    }
+
+    #[test]
+    fn location_works() {
+        check_expect(
+            InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG },
+            r#"
+//- minicore: sized
+fn foo<T>() {}
+"#,
+            expect![[r#"
+                [
+                    (
+                        7..8,
+                        [
+                            ": ",
+                            InlayHintLabelPart {
+                                text: "Sized",
+                                linked_location: Some(
+                                    FileRangeWrapper {
+                                        file_id: FileId(
+                                            1,
+                                        ),
+                                        range: 135..140,
+                                    },
+                                ),
+                                tooltip: "",
+                            },
+                        ],
+                    ),
+                ]
+            "#]],
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
index 028ed1650f4..8eeacb6e8d4 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/chaining.rs
@@ -81,7 +81,7 @@ mod tests {
 
     use crate::{
         fixture,
-        inlay_hints::tests::{check_with_config, DISABLED_CONFIG, TEST_CONFIG},
+        inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
         InlayHintsConfig,
     };
 
@@ -91,15 +91,6 @@ mod tests {
     }
 
     #[track_caller]
-    pub(super) fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
-        let (analysis, file_id) = fixture::file(ra_fixture);
-        let inlay_hints = analysis.inlay_hints(&config, file_id, None).unwrap();
-        let filtered =
-            inlay_hints.into_iter().map(|hint| (hint.range, hint.label)).collect::<Vec<_>>();
-        expect.assert_debug_eq(&filtered)
-    }
-
-    #[track_caller]
     pub(super) fn check_expect_clear_loc(
         config: InlayHintsConfig,
         ra_fixture: &str,
diff --git a/src/tools/rust-analyzer/crates/ide/src/static_index.rs b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
index 700e166b238..60d8259c53d 100644
--- a/src/tools/rust-analyzer/crates/ide/src/static_index.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/static_index.rs
@@ -138,6 +138,7 @@ impl StaticIndex<'_> {
                     render_colons: true,
                     discriminant_hints: crate::DiscriminantHints::Fieldless,
                     type_hints: true,
+                    sized_bound: false,
                     parameter_hints: true,
                     generic_parameter_hints: crate::GenericParameterHints {
                         type_hints: false,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index afe3455b780..bcaec520195 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -1051,6 +1051,7 @@ impl flags::AnalysisStats {
                 &InlayHintsConfig {
                     render_colons: false,
                     type_hints: true,
+                    sized_bound: false,
                     discriminant_hints: ide::DiscriminantHints::Always,
                     parameter_hints: true,
                     generic_parameter_hints: ide::GenericParameterHints {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 9c5af5ff6a5..72d021db5a8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -185,6 +185,8 @@ config_data! {
         inlayHints_genericParameterHints_type_enable: bool = false,
         /// Whether to show implicit drop hints.
         inlayHints_implicitDrops_enable: bool                      = false,
+        /// Whether to show inlay hints for the implied type parameter `Sized` bound.
+        inlayHints_implicitSizedBoundHints_enable: bool            = false,
         /// Whether to show inlay type hints for elided lifetimes in function signatures.
         inlayHints_lifetimeElisionHints_enable: LifetimeElisionDef = LifetimeElisionDef::Never,
         /// Whether to prefer using parameter names as the name for elided lifetime hints if possible.
@@ -1621,6 +1623,7 @@ impl Config {
         InlayHintsConfig {
             render_colons: self.inlayHints_renderColons().to_owned(),
             type_hints: self.inlayHints_typeHints_enable().to_owned(),
+            sized_bound: self.inlayHints_implicitSizedBoundHints_enable().to_owned(),
             parameter_hints: self.inlayHints_parameterHints_enable().to_owned(),
             generic_parameter_hints: GenericParameterHints {
                 type_hints: self.inlayHints_genericParameterHints_type_enable().to_owned(),
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index acaf43b987e..45eb38cd4f8 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -716,6 +716,11 @@ Whether to show generic type parameter name inlay hints.
 --
 Whether to show implicit drop hints.
 --
+[[rust-analyzer.inlayHints.implicitSizedBoundHints.enable]]rust-analyzer.inlayHints.implicitSizedBoundHints.enable (default: `false`)::
++
+--
+Whether to show inlay hints for the implied type parameter `Sized` bound.
+--
 [[rust-analyzer.inlayHints.lifetimeElisionHints.enable]]rust-analyzer.inlayHints.lifetimeElisionHints.enable (default: `"never"`)::
 +
 --
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 26cd49d9d2a..76d85a661e3 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2108,6 +2108,16 @@
             {
                 "title": "inlayHints",
                 "properties": {
+                    "rust-analyzer.inlayHints.implicitSizedBoundHints.enable": {
+                        "markdownDescription": "Whether to show inlay hints for the implied type parameter `Sized` bound.",
+                        "default": false,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
+                "title": "inlayHints",
+                "properties": {
                     "rust-analyzer.inlayHints.lifetimeElisionHints.enable": {
                         "markdownDescription": "Whether to show inlay type hints for elided lifetimes in function signatures.",
                         "default": "never",