1 //! Implementation of "chaining" inlay hints.
2 use hir::{HirDisplay, Semantics};
3 use ide_db::{famous_defs::FamousDefs, RootDatabase};
6 Direction, NodeOrToken, SyntaxKind, T,
10 inlay_hints::hint_iterator, FileId, InlayHint, InlayHintsConfig, InlayKind, InlayTooltip,
14 acc: &mut Vec<InlayHint>,
15 sema: &Semantics<'_, RootDatabase>,
16 famous_defs: &FamousDefs<'_, '_>,
17 config: &InlayHintsConfig,
21 if !config.chaining_hints {
25 if matches!(expr, ast::Expr::RecordExpr(_)) {
29 let descended = sema.descend_node_into_attributes(expr.clone()).pop();
30 let desc_expr = descended.as_ref().unwrap_or(expr);
34 .siblings_with_tokens(Direction::Next)
35 .filter_map(NodeOrToken::into_token)
36 .filter(|t| match t.kind() {
37 SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
38 SyntaxKind::COMMENT => false,
42 // Chaining can be defined as an expression whose next sibling tokens are newline and dot
43 // Ignoring extra whitespace and comments
44 let next = tokens.next()?.kind();
45 if next == SyntaxKind::WHITESPACE {
46 let mut next_next = tokens.next()?.kind();
47 while next_next == SyntaxKind::WHITESPACE {
48 next_next = tokens.next()?.kind();
50 if next_next == T![.] {
51 let ty = sema.type_of_expr(desc_expr)?.original;
55 if matches!(expr, ast::Expr::PathExpr(_)) {
56 if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
57 if st.fields(sema.db).is_empty() {
63 range: expr.syntax().text_range(),
64 kind: InlayKind::ChainingHint,
65 label: hint_iterator(sema, &famous_defs, config, &ty)
66 .unwrap_or_else(|| ty.display_truncated(sema.db, config.max_length).to_string())
68 tooltip: Some(InlayTooltip::HoverRanged(file_id, expr.syntax().text_range())),
77 use expect_test::expect;
80 inlay_hints::tests::{check_expect, check_with_config, DISABLED_CONFIG, TEST_CONFIG},
85 fn check_chains(ra_fixture: &str) {
86 check_with_config(InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG }, ra_fixture);
90 fn chaining_hints_ignore_comments() {
92 InlayHintsConfig { type_hints: false, chaining_hints: true, ..DISABLED_CONFIG },
95 impl A { fn into_b(self) -> B { self.0 } }
97 impl B { fn into_c(self) -> C { self.0 } }
102 .into_b() // This is a comment
103 // This is another comment
145 fn chaining_hints_without_newlines() {
149 impl A { fn into_b(self) -> B { self.0 } }
151 impl B { fn into_c(self) -> C { self.0 } }
155 let c = A(B(C)).into_b().into_c();
161 fn struct_access_chaining_hints() {
163 InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
165 struct A { pub b: B }
166 struct B { pub c: C }
171 fn foo(&self) -> i32 { 42 }
175 let x = A { b: B { c: C(true) } }
220 fn generic_chaining_hints() {
222 InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
230 fn new(t: T) -> Self { A(t) }
231 fn into_b(self) -> B<T> { B(self.0) }
234 fn into_c(self) -> C<T> { C(self.0) }
237 let c = A::new(X(42, true))
280 fn shorten_iterator_chaining_hints() {
282 InlayHintsConfig { chaining_hints: true, ..DISABLED_CONFIG },
284 //- minicore: iterators
289 impl Iterator for MyIter {
291 fn next(&mut self) -> Option<Self::Item> {
297 let _x = MyIter.by_ref()
310 "impl Iterator<Item = ()>",
325 "impl Iterator<Item = ()>",
340 "impl Iterator<Item = ()>",
372 fn hints_in_attr_call() {
376 //- proc_macros: identity, input_replace
379 fn chain(self) -> Self {
383 #[proc_macros::identity]
390 Struct::chain(strukt);