about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLukas Wirth <me@lukaswirth.dev>2025-06-04 11:30:43 +0200
committerLukas Wirth <me@lukaswirth.dev>2025-06-04 11:40:05 +0200
commitefa88e52506264c3744a08c841b7a0d8f85b5a11 (patch)
treee4cddf596e1d0ff37c05711867c9737dc85d168c
parent25cef03fdfe7b1943ca1b992954d3c32accc7769 (diff)
downloadrust-efa88e52506264c3744a08c841b7a0d8f85b5a11.tar.gz
rust-efa88e52506264c3744a08c841b7a0d8f85b5a11.zip
feat: Add `dyn` keyword inlay hints
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs133
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs2
3 files changed, 142 insertions, 2 deletions
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 d05a36c5f4b..b094b098462 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs
@@ -34,6 +34,7 @@ mod extern_block;
 mod generic_param;
 mod implicit_drop;
 mod implicit_static;
+mod implied_dyn_trait;
 mod lifetime;
 mod param_name;
 mod range_exclusive;
@@ -275,7 +276,12 @@ fn hints(
             ast::Type(ty) => match ty {
                 ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config,  ptr),
                 ast::Type::PathType(path) => {
-                    lifetime::fn_path_hints(hints, ctx, famous_defs, config,  path);
+                    lifetime::fn_path_hints(hints, ctx, famous_defs, config, &path);
+                    implied_dyn_trait::hints(hints, famous_defs, config, Either::Left(path));
+                    Some(())
+                },
+                ast::Type::DynTraitType(dyn_) => {
+                    implied_dyn_trait::hints(hints, famous_defs, config, Either::Right(dyn_));
                     Some(())
                 },
                 _ => Some(()),
@@ -445,6 +451,7 @@ pub enum InlayKind {
     Parameter,
     GenericParameter,
     Type,
+    Dyn,
     Drop,
     RangeExclusive,
     ExternUnsafety,
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
new file mode 100644
index 00000000000..32d130503a4
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -0,0 +1,133 @@
+//! Implementation of trait bound hints.
+//!
+//! Currently this renders the implied `Sized` bound.
+use either::Either;
+use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
+
+use syntax::ast::{self, AstNode};
+
+use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
+
+pub(super) fn hints(
+    acc: &mut Vec<InlayHint>,
+    FamousDefs(sema, _): &FamousDefs<'_, '_>,
+    config: &InlayHintsConfig,
+    path: Either<ast::PathType, ast::DynTraitType>,
+) -> Option<()> {
+    let parent = path.syntax().parent()?;
+    let range = match path {
+        Either::Left(path) => {
+            let paren =
+                parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
+            let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
+            if ast::TypeBound::can_cast(parent.kind())
+                || ast::TypeAnchor::can_cast(parent.kind())
+                || ast::Impl::cast(parent)
+                    .and_then(|it| it.trait_())
+                    .is_some_and(|it| it.syntax() == path.syntax())
+            {
+                return None;
+            }
+            sema.resolve_trait(&path.path()?)?;
+            paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
+        }
+        Either::Right(dyn_) => {
+            if dyn_.dyn_token().is_some() {
+                return None;
+            }
+
+            dyn_.syntax().text_range()
+        }
+    };
+
+    acc.push(InlayHint {
+        range,
+        kind: InlayKind::Dyn,
+        label: InlayHintLabel::simple("dyn", None, None),
+        text_edit: Some(
+            config.lazy_text_edit(|| TextEdit::insert(range.start(), "dyn ".to_owned())),
+        ),
+        position: InlayHintPosition::Before,
+        pad_left: false,
+        pad_right: true,
+        resolve_parent: Some(range),
+    });
+
+    Some(())
+}
+
+#[cfg(test)]
+mod tests {
+
+    use expect_test::expect;
+
+    use crate::inlay_hints::InlayHintsConfig;
+
+    use crate::inlay_hints::tests::{DISABLED_CONFIG, check_edit, check_with_config};
+
+    #[track_caller]
+    fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
+        check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
+    }
+
+    #[test]
+    fn path_works() {
+        check(
+            r#"
+struct S {}
+trait T {}
+fn foo(_: T,  _: dyn T, _: S) {}
+       // ^ dyn
+fn foo(_: &T,  _: for<'a> T) {}
+        // ^ dyn
+                       // ^ dyn
+impl T {}
+  // ^ dyn
+impl T for (T) {}
+        // ^^^ dyn
+"#,
+        );
+    }
+
+    #[test]
+    fn missing_dyn_bounds() {
+        check(
+            r#"
+trait T {}
+fn foo(
+    _: T + T,
+    // ^^^^^ dyn
+    _: T + 'a,
+    // ^^^^^^ dyn
+    _: 'a + T,
+    // ^^^^^^ dyn
+    _: &(T + T)
+    //   ^^^^^ dyn
+    _: &mut (T + T)
+    //       ^^^^^ dyn
+    _: *mut (T),
+    //      ^^^ dyn
+) {}
+"#,
+        );
+    }
+
+    #[test]
+    fn edit() {
+        check_edit(
+            DISABLED_CONFIG,
+            r#"
+trait T {}
+fn foo(
+    _: &mut T
+) {}
+"#,
+            expect![[r#"
+                trait T {}
+                fn foo(
+                    _: &mut dyn T
+                ) {}
+            "#]],
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
index 939fe026919..0069452e7b9 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs
@@ -135,7 +135,7 @@ pub(super) fn fn_path_hints(
     ctx: &mut InlayHintCtx,
     fd: &FamousDefs<'_, '_>,
     config: &InlayHintsConfig,
-    func: ast::PathType,
+    func: &ast::PathType,
 ) -> Option<()> {
     if config.lifetime_elision_hints == LifetimeElisionHints::Never {
         return None;