Unnamed repository; edit this file 'description' to name the repository.
feat: Add `dyn` keyword inlay hints
Lukas Wirth 11 months ago
parent 7c3de05 · commit b973381
-rw-r--r--crates/ide/src/inlay_hints.rs9
-rw-r--r--crates/ide/src/inlay_hints/implied_dyn_trait.rs133
-rw-r--r--crates/ide/src/inlay_hints/lifetime.rs2
3 files changed, 142 insertions, 2 deletions
diff --git a/crates/ide/src/inlay_hints.rs b/crates/ide/src/inlay_hints.rs
index d05a36c5f4..b094b09846 100644
--- a/crates/ide/src/inlay_hints.rs
+++ b/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/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/crates/ide/src/inlay_hints/implied_dyn_trait.rs
new file mode 100644
index 0000000000..32d130503a
--- /dev/null
+++ b/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/crates/ide/src/inlay_hints/lifetime.rs b/crates/ide/src/inlay_hints/lifetime.rs
index 939fe02691..0069452e7b 100644
--- a/crates/ide/src/inlay_hints/lifetime.rs
+++ b/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;