Skip to content

Commit 9dfb9df

Browse files
committed
Auto merge of rust-lang#13804 - WaffleLapkin:inlay_hint_mods, r=Veykril
Split inlay hints into modules per hint type I think this makes the code a lot easier to maintain.
2 parents ccbf8fe + 046a567 commit 9dfb9df

10 files changed

+3003
-2823
lines changed

crates/ide/src/inlay_hints.rs

Lines changed: 60 additions & 2823 deletions
Large diffs are not rendered by default.
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
//! Implementation of "adjustment" inlay hints:
2+
//! ```no_run
3+
//! let _: u32 = /* <never-to-any> */ loop {};
4+
//! let _: &u32 = /* &* */ &mut 0;
5+
//! ```
6+
use hir::{Adjust, AutoBorrow, Mutability, OverloadedDeref, PointerCast, Safety, Semantics};
7+
use ide_db::RootDatabase;
8+
9+
use syntax::ast::{self, AstNode};
10+
11+
use crate::{AdjustmentHints, InlayHint, InlayHintsConfig, InlayKind};
12+
13+
pub(super) fn hints(
14+
acc: &mut Vec<InlayHint>,
15+
sema: &Semantics<'_, RootDatabase>,
16+
config: &InlayHintsConfig,
17+
expr: &ast::Expr,
18+
) -> Option<()> {
19+
if config.adjustment_hints == AdjustmentHints::Never {
20+
return None;
21+
}
22+
23+
// These inherit from the inner expression which would result in duplicate hints
24+
if let ast::Expr::ParenExpr(_)
25+
| ast::Expr::IfExpr(_)
26+
| ast::Expr::BlockExpr(_)
27+
| ast::Expr::MatchExpr(_) = expr
28+
{
29+
return None;
30+
}
31+
32+
let parent = expr.syntax().parent().and_then(ast::Expr::cast);
33+
let descended = sema.descend_node_into_attributes(expr.clone()).pop();
34+
let desc_expr = descended.as_ref().unwrap_or(expr);
35+
let adjustments = sema.expr_adjustments(desc_expr).filter(|it| !it.is_empty())?;
36+
let needs_parens = match parent {
37+
Some(parent) => {
38+
match parent {
39+
ast::Expr::AwaitExpr(_)
40+
| ast::Expr::CallExpr(_)
41+
| ast::Expr::CastExpr(_)
42+
| ast::Expr::FieldExpr(_)
43+
| ast::Expr::MethodCallExpr(_)
44+
| ast::Expr::TryExpr(_) => true,
45+
// FIXME: shorthands need special casing, though not sure if adjustments are even valid there
46+
ast::Expr::RecordExpr(_) => false,
47+
ast::Expr::IndexExpr(index) => index.base().as_ref() == Some(expr),
48+
_ => false,
49+
}
50+
}
51+
None => false,
52+
};
53+
if needs_parens {
54+
acc.push(InlayHint {
55+
range: expr.syntax().text_range(),
56+
kind: InlayKind::OpeningParenthesis,
57+
label: "(".into(),
58+
tooltip: None,
59+
});
60+
}
61+
for adjustment in adjustments.into_iter().rev() {
62+
// FIXME: Add some nicer tooltips to each of these
63+
let text = match adjustment {
64+
Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
65+
"<never-to-any>"
66+
}
67+
Adjust::Deref(None) => "*",
68+
Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => "*",
69+
Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => "*",
70+
Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => "&",
71+
Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => "&mut ",
72+
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => "&raw const ",
73+
Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => "&raw mut ",
74+
// some of these could be represented via `as` casts, but that's not too nice and
75+
// handling everything as a prefix expr makes the `(` and `)` insertion easier
76+
Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
77+
match cast {
78+
PointerCast::ReifyFnPointer => "<fn-item-to-fn-pointer>",
79+
PointerCast::UnsafeFnPointer => "<safe-fn-pointer-to-unsafe-fn-pointer>",
80+
PointerCast::ClosureFnPointer(Safety::Unsafe) => {
81+
"<closure-to-unsafe-fn-pointer>"
82+
}
83+
PointerCast::ClosureFnPointer(Safety::Safe) => "<closure-to-fn-pointer>",
84+
PointerCast::MutToConstPointer => "<mut-ptr-to-const-ptr>",
85+
PointerCast::ArrayToPointer => "<array-ptr-to-element-ptr>",
86+
PointerCast::Unsize => "<unsize>",
87+
}
88+
}
89+
_ => continue,
90+
};
91+
acc.push(InlayHint {
92+
range: expr.syntax().text_range(),
93+
kind: InlayKind::AdjustmentHint,
94+
label: text.into(),
95+
tooltip: None,
96+
});
97+
}
98+
if needs_parens {
99+
acc.push(InlayHint {
100+
range: expr.syntax().text_range(),
101+
kind: InlayKind::ClosingParenthesis,
102+
label: ")".into(),
103+
tooltip: None,
104+
});
105+
}
106+
Some(())
107+
}
108+
109+
#[cfg(test)]
110+
mod tests {
111+
use crate::{
112+
inlay_hints::tests::{check_with_config, DISABLED_CONFIG},
113+
AdjustmentHints, InlayHintsConfig,
114+
};
115+
116+
#[test]
117+
fn adjustment_hints() {
118+
check_with_config(
119+
InlayHintsConfig { adjustment_hints: AdjustmentHints::Always, ..DISABLED_CONFIG },
120+
r#"
121+
//- minicore: coerce_unsized
122+
fn main() {
123+
let _: u32 = loop {};
124+
//^^^^^^^<never-to-any>
125+
let _: &u32 = &mut 0;
126+
//^^^^^^&
127+
//^^^^^^*
128+
let _: &mut u32 = &mut 0;
129+
//^^^^^^&mut $
130+
//^^^^^^*
131+
let _: *const u32 = &mut 0;
132+
//^^^^^^&raw const $
133+
//^^^^^^*
134+
let _: *mut u32 = &mut 0;
135+
//^^^^^^&raw mut $
136+
//^^^^^^*
137+
let _: fn() = main;
138+
//^^^^<fn-item-to-fn-pointer>
139+
let _: unsafe fn() = main;
140+
//^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
141+
//^^^^<fn-item-to-fn-pointer>
142+
let _: unsafe fn() = main as fn();
143+
//^^^^^^^^^^^^<safe-fn-pointer-to-unsafe-fn-pointer>
144+
let _: fn() = || {};
145+
//^^^^^<closure-to-fn-pointer>
146+
let _: unsafe fn() = || {};
147+
//^^^^^<closure-to-unsafe-fn-pointer>
148+
let _: *const u32 = &mut 0u32 as *mut u32;
149+
//^^^^^^^^^^^^^^^^^^^^^<mut-ptr-to-const-ptr>
150+
let _: &mut [_] = &mut [0; 0];
151+
//^^^^^^^^^^^<unsize>
152+
//^^^^^^^^^^^&mut $
153+
//^^^^^^^^^^^*
154+
155+
Struct.consume();
156+
Struct.by_ref();
157+
//^^^^^^(
158+
//^^^^^^&
159+
//^^^^^^)
160+
Struct.by_ref_mut();
161+
//^^^^^^(
162+
//^^^^^^&mut $
163+
//^^^^^^)
164+
165+
(&Struct).consume();
166+
//^^^^^^^*
167+
(&Struct).by_ref();
168+
169+
(&mut Struct).consume();
170+
//^^^^^^^^^^^*
171+
(&mut Struct).by_ref();
172+
//^^^^^^^^^^^&
173+
//^^^^^^^^^^^*
174+
(&mut Struct).by_ref_mut();
175+
176+
// Check that block-like expressions don't duplicate hints
177+
let _: &mut [u32] = (&mut []);
178+
//^^^^^^^<unsize>
179+
//^^^^^^^&mut $
180+
//^^^^^^^*
181+
let _: &mut [u32] = { &mut [] };
182+
//^^^^^^^<unsize>
183+
//^^^^^^^&mut $
184+
//^^^^^^^*
185+
let _: &mut [u32] = unsafe { &mut [] };
186+
//^^^^^^^<unsize>
187+
//^^^^^^^&mut $
188+
//^^^^^^^*
189+
let _: &mut [u32] = if true {
190+
&mut []
191+
//^^^^^^^<unsize>
192+
//^^^^^^^&mut $
193+
//^^^^^^^*
194+
} else {
195+
loop {}
196+
//^^^^^^^<never-to-any>
197+
};
198+
let _: &mut [u32] = match () { () => &mut [] }
199+
//^^^^^^^<unsize>
200+
//^^^^^^^&mut $
201+
//^^^^^^^*
202+
}
203+
204+
#[derive(Copy, Clone)]
205+
struct Struct;
206+
impl Struct {
207+
fn consume(self) {}
208+
fn by_ref(&self) {}
209+
fn by_ref_mut(&mut self) {}
210+
}
211+
trait Trait {}
212+
impl Trait for Struct {}
213+
"#,
214+
)
215+
}
216+
}

0 commit comments

Comments
 (0)