Skip to content

Commit efa88e5

Browse files
committed
feat: Add dyn keyword inlay hints
1 parent 25cef03 commit efa88e5

File tree

3 files changed

+142
-2
lines changed

3 files changed

+142
-2
lines changed

src/tools/rust-analyzer/crates/ide/src/inlay_hints.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ mod extern_block;
3434
mod generic_param;
3535
mod implicit_drop;
3636
mod implicit_static;
37+
mod implied_dyn_trait;
3738
mod lifetime;
3839
mod param_name;
3940
mod range_exclusive;
@@ -275,7 +276,12 @@ fn hints(
275276
ast::Type(ty) => match ty {
276277
ast::Type::FnPtrType(ptr) => lifetime::fn_ptr_hints(hints, ctx, famous_defs, config, ptr),
277278
ast::Type::PathType(path) => {
278-
lifetime::fn_path_hints(hints, ctx, famous_defs, config, path);
279+
lifetime::fn_path_hints(hints, ctx, famous_defs, config, &path);
280+
implied_dyn_trait::hints(hints, famous_defs, config, Either::Left(path));
281+
Some(())
282+
},
283+
ast::Type::DynTraitType(dyn_) => {
284+
implied_dyn_trait::hints(hints, famous_defs, config, Either::Right(dyn_));
279285
Some(())
280286
},
281287
_ => Some(()),
@@ -445,6 +451,7 @@ pub enum InlayKind {
445451
Parameter,
446452
GenericParameter,
447453
Type,
454+
Dyn,
448455
Drop,
449456
RangeExclusive,
450457
ExternUnsafety,
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//! Implementation of trait bound hints.
2+
//!
3+
//! Currently this renders the implied `Sized` bound.
4+
use either::Either;
5+
use ide_db::{famous_defs::FamousDefs, text_edit::TextEdit};
6+
7+
use syntax::ast::{self, AstNode};
8+
9+
use crate::{InlayHint, InlayHintLabel, InlayHintPosition, InlayHintsConfig, InlayKind};
10+
11+
pub(super) fn hints(
12+
acc: &mut Vec<InlayHint>,
13+
FamousDefs(sema, _): &FamousDefs<'_, '_>,
14+
config: &InlayHintsConfig,
15+
path: Either<ast::PathType, ast::DynTraitType>,
16+
) -> Option<()> {
17+
let parent = path.syntax().parent()?;
18+
let range = match path {
19+
Either::Left(path) => {
20+
let paren =
21+
parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
22+
let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
23+
if ast::TypeBound::can_cast(parent.kind())
24+
|| ast::TypeAnchor::can_cast(parent.kind())
25+
|| ast::Impl::cast(parent)
26+
.and_then(|it| it.trait_())
27+
.is_some_and(|it| it.syntax() == path.syntax())
28+
{
29+
return None;
30+
}
31+
sema.resolve_trait(&path.path()?)?;
32+
paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
33+
}
34+
Either::Right(dyn_) => {
35+
if dyn_.dyn_token().is_some() {
36+
return None;
37+
}
38+
39+
dyn_.syntax().text_range()
40+
}
41+
};
42+
43+
acc.push(InlayHint {
44+
range,
45+
kind: InlayKind::Dyn,
46+
label: InlayHintLabel::simple("dyn", None, None),
47+
text_edit: Some(
48+
config.lazy_text_edit(|| TextEdit::insert(range.start(), "dyn ".to_owned())),
49+
),
50+
position: InlayHintPosition::Before,
51+
pad_left: false,
52+
pad_right: true,
53+
resolve_parent: Some(range),
54+
});
55+
56+
Some(())
57+
}
58+
59+
#[cfg(test)]
60+
mod tests {
61+
62+
use expect_test::expect;
63+
64+
use crate::inlay_hints::InlayHintsConfig;
65+
66+
use crate::inlay_hints::tests::{DISABLED_CONFIG, check_edit, check_with_config};
67+
68+
#[track_caller]
69+
fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
70+
check_with_config(InlayHintsConfig { sized_bound: true, ..DISABLED_CONFIG }, ra_fixture);
71+
}
72+
73+
#[test]
74+
fn path_works() {
75+
check(
76+
r#"
77+
struct S {}
78+
trait T {}
79+
fn foo(_: T, _: dyn T, _: S) {}
80+
// ^ dyn
81+
fn foo(_: &T, _: for<'a> T) {}
82+
// ^ dyn
83+
// ^ dyn
84+
impl T {}
85+
// ^ dyn
86+
impl T for (T) {}
87+
// ^^^ dyn
88+
"#,
89+
);
90+
}
91+
92+
#[test]
93+
fn missing_dyn_bounds() {
94+
check(
95+
r#"
96+
trait T {}
97+
fn foo(
98+
_: T + T,
99+
// ^^^^^ dyn
100+
_: T + 'a,
101+
// ^^^^^^ dyn
102+
_: 'a + T,
103+
// ^^^^^^ dyn
104+
_: &(T + T)
105+
// ^^^^^ dyn
106+
_: &mut (T + T)
107+
// ^^^^^ dyn
108+
_: *mut (T),
109+
// ^^^ dyn
110+
) {}
111+
"#,
112+
);
113+
}
114+
115+
#[test]
116+
fn edit() {
117+
check_edit(
118+
DISABLED_CONFIG,
119+
r#"
120+
trait T {}
121+
fn foo(
122+
_: &mut T
123+
) {}
124+
"#,
125+
expect![[r#"
126+
trait T {}
127+
fn foo(
128+
_: &mut dyn T
129+
) {}
130+
"#]],
131+
);
132+
}
133+
}

src/tools/rust-analyzer/crates/ide/src/inlay_hints/lifetime.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ pub(super) fn fn_path_hints(
135135
ctx: &mut InlayHintCtx,
136136
fd: &FamousDefs<'_, '_>,
137137
config: &InlayHintsConfig,
138-
func: ast::PathType,
138+
func: &ast::PathType,
139139
) -> Option<()> {
140140
if config.lifetime_elision_hints == LifetimeElisionHints::Never {
141141
return None;

0 commit comments

Comments
 (0)