10231: feat: Make inlay hints work in attributed items r=Veykril a=Veykril

![image](https://user-images.githubusercontent.com/3757771/133172697-8563329f-e77e-46e4-86ab-99b50040dfd5.png)
Note the lack of chaining hints, this is currently due to macro expansion lacking the input whitespace. We might be able to recover this from the input somehow in the future.
Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10043
bors r+

Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2021-09-14 00:07:50 +00:00 committed by GitHub
commit e08b3bf70b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 98 additions and 16 deletions

View file

@ -211,6 +211,7 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
) -> impl Iterator<Item = SyntaxNode> + '_ {
token.parent().into_iter().flat_map(move |it| self.ancestors_with_macros(it))
}
pub fn ancestors_with_macros(&self, node: SyntaxNode) -> impl Iterator<Item = SyntaxNode> + '_ {
self.imp.ancestors_with_macros(node)
}

View file

@ -64,17 +64,35 @@ pub(crate) fn inlay_hints(
let file = sema.parse(file_id);
let mut res = Vec::new();
for node in file.syntax().descendants() {
if let Some(expr) = ast::Expr::cast(node.clone()) {
get_chaining_hints(&mut res, &sema, config, expr);
}
let mut queue = vec![file.syntax().preorder()];
match_ast! {
match node {
ast::CallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
ast::MethodCallExpr(it) => { get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it)); },
ast::IdentPat(it) => { get_bind_pat_hints(&mut res, &sema, config, it); },
_ => (),
while let Some(mut preorder) = queue.pop() {
while let Some(event) = preorder.next() {
let node = match event {
syntax::WalkEvent::Enter(node) => node,
syntax::WalkEvent::Leave(_) => continue,
};
if let Some(node) =
ast::Item::cast(node.clone()).and_then(|item| sema.expand_attr_macro(&item))
{
preorder.skip_subtree();
queue.push(node.preorder());
continue;
}
if let Some(expr) = ast::Expr::cast(node.clone()) {
get_chaining_hints(&mut res, &sema, config, &expr);
match expr {
ast::Expr::CallExpr(it) => {
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
}
ast::Expr::MethodCallExpr(it) => {
get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
}
_ => (),
}
} else if let Some(it) = ast::IdentPat::cast(node.clone()) {
get_bind_pat_hints(&mut res, &sema, config, it);
}
}
}
@ -85,7 +103,7 @@ fn get_chaining_hints(
acc: &mut Vec<InlayHint>,
sema: &Semantics<RootDatabase>,
config: &InlayHintsConfig,
expr: ast::Expr,
expr: &ast::Expr,
) -> Option<()> {
if !config.chaining_hints {
return None;
@ -117,7 +135,7 @@ fn get_chaining_hints(
next_next = tokens.next()?.kind();
}
if next_next == T![.] {
let ty = sema.type_of_expr(&expr)?.original;
let ty = sema.type_of_expr(expr)?.original;
if ty.is_unknown() {
return None;
}
@ -129,7 +147,7 @@ fn get_chaining_hints(
}
}
acc.push(InlayHint {
range: expr.syntax().text_range(),
range: sema.original_range(expr.syntax()).range,
kind: InlayKind::ChainingHint,
label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| {
ty.display_truncated(sema.db, config.max_length).to_string().into()
@ -167,7 +185,7 @@ fn get_param_name_hints(
})
.filter(|(param_name, arg)| !should_hide_param_name_hint(sema, &callable, param_name, arg))
.map(|(param_name, arg)| InlayHint {
range: arg.syntax().text_range(),
range: sema.original_range(arg.syntax()).range,
kind: InlayKind::ParameterHint,
label: param_name.into(),
});
@ -197,8 +215,8 @@ fn get_bind_pat_hints(
acc.push(InlayHint {
range: match pat.name() {
Some(name) => name.syntax().text_range(),
None => pat.syntax().text_range(),
Some(name) => sema.original_range(name.syntax()).range,
None => sema.original_range(pat.syntax()).range,
},
kind: InlayKind::TypeHint,
label: hint_iterator(sema, &famous_defs, config, &ty)
@ -1467,4 +1485,67 @@ fn main() {
"#]],
);
}
#[test]
fn hints_in_attr_call() {
// chaining hints do not currently work as macros lose all whitespace information
check_expect(
TEST_CONFIG,
r#"
//- proc_macros: identity, input_replace
struct Struct;
impl Struct {
fn chain(self) -> Self {
self
}
}
#[proc_macros::identity]
fn main() {
let strukt = Struct;
strukt
.chain()
.chain()
.chain();
Struct::chain(strukt);
}
#[proc_macros::input_replace(
fn not_main() {
let strukt = Struct;
strukt
.chain()
.chain()
.chain();
Struct::chain(strukt);
}
)]
fn main() {}
"#,
expect![[r#"
[
InlayHint {
range: 297..303,
kind: TypeHint,
label: "Struct",
},
InlayHint {
range: 415..421,
kind: ParameterHint,
label: "self",
},
InlayHint {
range: 125..131,
kind: TypeHint,
label: "Struct",
},
InlayHint {
range: 223..229,
kind: ParameterHint,
label: "self",
},
]
"#]],
);
}
}