]> git.lizzy.rs Git - rust.git/blob - crates/ide/src/inlay_hints.rs
Merge #10387
[rust.git] / crates / ide / src / inlay_hints.rs
1 use either::Either;
2 use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
3 use ide_db::RootDatabase;
4 use ide_db::{base_db::FileRange, helpers::FamousDefs};
5 use itertools::Itertools;
6 use stdx::to_lower_snake_case;
7 use syntax::{
8     ast::{self, AstNode, HasArgList, HasName},
9     match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
10 };
11
12 use crate::FileId;
13
14 #[derive(Clone, Debug, PartialEq, Eq)]
15 pub struct InlayHintsConfig {
16     pub type_hints: bool,
17     pub parameter_hints: bool,
18     pub chaining_hints: bool,
19     pub max_length: Option<usize>,
20 }
21
22 #[derive(Clone, Debug, PartialEq, Eq)]
23 pub enum InlayKind {
24     TypeHint,
25     ParameterHint,
26     ChainingHint,
27 }
28
29 #[derive(Debug)]
30 pub struct InlayHint {
31     pub range: TextRange,
32     pub kind: InlayKind,
33     pub label: SmolStr,
34 }
35
36 // Feature: Inlay Hints
37 //
38 // rust-analyzer shows additional information inline with the source code.
39 // Editors usually render this using read-only virtual text snippets interspersed with code.
40 //
41 // rust-analyzer shows hints for
42 //
43 // * types of local variables
44 // * names of function arguments
45 // * types of chained expressions
46 //
47 // **Note:** VS Code does not have native support for inlay hints https://github.com/microsoft/vscode/issues/16221[yet] and the hints are implemented using decorations.
48 // This approach has limitations, the caret movement and bracket highlighting near the edges of the hint may be weird:
49 // https://github.com/rust-analyzer/rust-analyzer/issues/1623[1], https://github.com/rust-analyzer/rust-analyzer/issues/3453[2].
50 //
51 // |===
52 // | Editor  | Action Name
53 //
54 // | VS Code | **Rust Analyzer: Toggle inlay hints*
55 // |===
56 //
57 // image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
58 pub(crate) fn inlay_hints(
59     db: &RootDatabase,
60     file_id: FileId,
61     config: &InlayHintsConfig,
62 ) -> Vec<InlayHint> {
63     let _p = profile::span("inlay_hints");
64     let sema = Semantics::new(db);
65     let file = sema.parse(file_id);
66     let file = file.syntax();
67
68     let mut res = Vec::new();
69
70     for node in file.descendants() {
71         if let Some(expr) = ast::Expr::cast(node.clone()) {
72             get_chaining_hints(&mut res, &sema, config, &expr);
73             match expr {
74                 ast::Expr::CallExpr(it) => {
75                     get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
76                 }
77                 ast::Expr::MethodCallExpr(it) => {
78                     get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
79                 }
80                 _ => (),
81             }
82         } else if let Some(it) = ast::IdentPat::cast(node.clone()) {
83             get_bind_pat_hints(&mut res, &sema, config, &it);
84         }
85     }
86     res
87 }
88
89 fn get_chaining_hints(
90     acc: &mut Vec<InlayHint>,
91     sema: &Semantics<RootDatabase>,
92     config: &InlayHintsConfig,
93     expr: &ast::Expr,
94 ) -> Option<()> {
95     if !config.chaining_hints {
96         return None;
97     }
98
99     if matches!(expr, ast::Expr::RecordExpr(_)) {
100         return None;
101     }
102
103     let descended = sema.descend_node_into_attributes(expr.clone()).pop();
104     let desc_expr = descended.as_ref().unwrap_or(expr);
105     let krate = sema.scope(desc_expr.syntax()).module().map(|it| it.krate());
106     let famous_defs = FamousDefs(sema, krate);
107
108     let mut tokens = expr
109         .syntax()
110         .siblings_with_tokens(Direction::Next)
111         .filter_map(NodeOrToken::into_token)
112         .filter(|t| match t.kind() {
113             SyntaxKind::WHITESPACE if !t.text().contains('\n') => false,
114             SyntaxKind::COMMENT => false,
115             _ => true,
116         });
117
118     // Chaining can be defined as an expression whose next sibling tokens are newline and dot
119     // Ignoring extra whitespace and comments
120     let next = tokens.next()?.kind();
121     if next == SyntaxKind::WHITESPACE {
122         let mut next_next = tokens.next()?.kind();
123         while next_next == SyntaxKind::WHITESPACE {
124             next_next = tokens.next()?.kind();
125         }
126         if next_next == T![.] {
127             let ty = sema.type_of_expr(desc_expr)?.original;
128             if ty.is_unknown() {
129                 return None;
130             }
131             if matches!(expr, ast::Expr::PathExpr(_)) {
132                 if let Some(hir::Adt::Struct(st)) = ty.as_adt() {
133                     if st.fields(sema.db).is_empty() {
134                         return None;
135                     }
136                 }
137             }
138             acc.push(InlayHint {
139                 range: expr.syntax().text_range(),
140                 kind: InlayKind::ChainingHint,
141                 label: hint_iterator(sema, &famous_defs, config, &ty).unwrap_or_else(|| {
142                     ty.display_truncated(sema.db, config.max_length).to_string().into()
143                 }),
144             });
145         }
146     }
147     Some(())
148 }
149
150 fn get_param_name_hints(
151     acc: &mut Vec<InlayHint>,
152     sema: &Semantics<RootDatabase>,
153     config: &InlayHintsConfig,
154     expr: ast::Expr,
155 ) -> Option<()> {
156     if !config.parameter_hints {
157         return None;
158     }
159
160     let (callable, arg_list) = get_callable(sema, &expr)?;
161     let hints = callable
162         .params(sema.db)
163         .into_iter()
164         .zip(arg_list.args())
165         .filter_map(|((param, _ty), arg)| {
166             // Only annotate hints for expressions that exist in the original file
167             let range = sema.original_range_opt(arg.syntax())?;
168             let param_name = match param? {
169                 Either::Left(_) => "self".to_string(),
170                 Either::Right(pat) => match pat {
171                     ast::Pat::IdentPat(it) => it.name()?.to_string(),
172                     _ => return None,
173                 },
174             };
175             Some((param_name, arg, range))
176         })
177         .filter(|(param_name, arg, _)| {
178             !should_hide_param_name_hint(sema, &callable, param_name, arg)
179         })
180         .map(|(param_name, _, FileRange { range, .. })| InlayHint {
181             range,
182             kind: InlayKind::ParameterHint,
183             label: param_name.into(),
184         });
185
186     acc.extend(hints);
187     Some(())
188 }
189
190 fn get_bind_pat_hints(
191     acc: &mut Vec<InlayHint>,
192     sema: &Semantics<RootDatabase>,
193     config: &InlayHintsConfig,
194     pat: &ast::IdentPat,
195 ) -> Option<()> {
196     if !config.type_hints {
197         return None;
198     }
199
200     let descended = sema.descend_node_into_attributes(pat.clone()).pop();
201     let desc_pat = descended.as_ref().unwrap_or(pat);
202     let ty = sema.type_of_pat(&desc_pat.clone().into())?.original;
203
204     if should_not_display_type_hint(sema, pat, &ty) {
205         return None;
206     }
207
208     let krate = sema.scope(desc_pat.syntax()).module().map(|it| it.krate());
209     let famous_defs = FamousDefs(sema, krate);
210     let label = hint_iterator(sema, &famous_defs, config, &ty);
211
212     let label = match label {
213         Some(label) => label,
214         None => {
215             let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
216             if is_named_constructor(sema, pat, &ty_name).is_some() {
217                 return None;
218             }
219             ty_name.into()
220         }
221     };
222
223     acc.push(InlayHint {
224         range: match pat.name() {
225             Some(name) => name.syntax().text_range(),
226             None => pat.syntax().text_range(),
227         },
228         kind: InlayKind::TypeHint,
229         label,
230     });
231
232     Some(())
233 }
234
235 fn is_named_constructor(
236     sema: &Semantics<RootDatabase>,
237     pat: &ast::IdentPat,
238     ty_name: &str,
239 ) -> Option<()> {
240     let let_node = pat.syntax().parent()?;
241     let expr = match_ast! {
242         match let_node {
243             ast::LetStmt(it) => it.initializer(),
244             ast::Condition(it) => it.expr(),
245             _ => None,
246         }
247     }?;
248
249     let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
250     // unwrap postfix expressions
251     let expr = match expr {
252         ast::Expr::TryExpr(it) => it.expr(),
253         ast::Expr::AwaitExpr(it) => it.expr(),
254         expr => Some(expr),
255     }?;
256     let expr = match expr {
257         ast::Expr::CallExpr(call) => match call.expr()? {
258             ast::Expr::PathExpr(p) => p,
259             _ => return None,
260         },
261         _ => return None,
262     };
263     let path = expr.path()?;
264
265     // Check for tuple-struct or tuple-variant in which case we can check the last segment
266     let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
267     let callable_kind = callable.map(|it| it.kind());
268     if let Some(hir::CallableKind::TupleStruct(_) | hir::CallableKind::TupleEnumVariant(_)) =
269         callable_kind
270     {
271         if let Some(ctor) = path.segment() {
272             return (ctor.to_string() == ty_name).then(|| ());
273         }
274     }
275
276     // otherwise use the qualifying segment as the constructor name
277     let qual_seg = path.qualifier()?.segment()?;
278     let ctor_name = match qual_seg.kind()? {
279         ast::PathSegmentKind::Name(name_ref) => {
280             match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
281                 Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
282                 None => name_ref.to_string(),
283             }
284         }
285         ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
286         _ => return None,
287     };
288     (ctor_name == ty_name).then(|| ())
289 }
290
291 /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
292 fn hint_iterator(
293     sema: &Semantics<RootDatabase>,
294     famous_defs: &FamousDefs,
295     config: &InlayHintsConfig,
296     ty: &hir::Type,
297 ) -> Option<SmolStr> {
298     let db = sema.db;
299     let strukt = ty.strip_references().as_adt()?;
300     let krate = strukt.module(db).krate();
301     if krate != famous_defs.core()? {
302         return None;
303     }
304     let iter_trait = famous_defs.core_iter_Iterator()?;
305     let iter_mod = famous_defs.core_iter()?;
306
307     // Assert that this struct comes from `core::iter`.
308     if !(strukt.visibility(db) == hir::Visibility::Public
309         && strukt.module(db).path_to_root(db).contains(&iter_mod))
310     {
311         return None;
312     }
313
314     if ty.impls_trait(db, iter_trait, &[]) {
315         let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
316             hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
317             _ => None,
318         })?;
319         if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) {
320             const LABEL_START: &str = "impl Iterator<Item = ";
321             const LABEL_END: &str = ">";
322
323             let ty_display = hint_iterator(sema, famous_defs, config, &ty)
324                 .map(|assoc_type_impl| assoc_type_impl.to_string())
325                 .unwrap_or_else(|| {
326                     ty.display_truncated(
327                         db,
328                         config
329                             .max_length
330                             .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
331                     )
332                     .to_string()
333                 });
334             return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into());
335         }
336     }
337
338     None
339 }
340
341 fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
342     if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
343         let pat_text = bind_pat.to_string();
344         enum_data
345             .variants(db)
346             .into_iter()
347             .map(|variant| variant.name(db).to_string())
348             .any(|enum_name| enum_name == pat_text)
349     } else {
350         false
351     }
352 }
353
354 fn should_not_display_type_hint(
355     sema: &Semantics<RootDatabase>,
356     bind_pat: &ast::IdentPat,
357     pat_ty: &hir::Type,
358 ) -> bool {
359     let db = sema.db;
360
361     if pat_ty.is_unknown() {
362         return true;
363     }
364
365     if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
366         if s.fields(db).is_empty() && s.name(db).to_string() == bind_pat.to_string() {
367             return true;
368         }
369     }
370
371     for node in bind_pat.syntax().ancestors() {
372         match_ast! {
373             match node {
374                 ast::LetStmt(it) => return it.ty().is_some(),
375                 ast::Param(it) => return it.ty().is_some(),
376                 ast::MatchArm(_it) => return pat_is_enum_variant(db, bind_pat, pat_ty),
377                 ast::IfExpr(it) => {
378                     return it.condition().and_then(|condition| condition.pat()).is_some()
379                         && pat_is_enum_variant(db, bind_pat, pat_ty);
380                 },
381                 ast::WhileExpr(it) => {
382                     return it.condition().and_then(|condition| condition.pat()).is_some()
383                         && pat_is_enum_variant(db, bind_pat, pat_ty);
384                 },
385                 ast::ForExpr(it) => {
386                     // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
387                     // Type of expr should be iterable.
388                     return it.in_token().is_none() ||
389                         it.iterable()
390                             .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
391                             .map(TypeInfo::original)
392                             .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
393                 },
394                 _ => (),
395             }
396         }
397     }
398     false
399 }
400
401 fn should_hide_param_name_hint(
402     sema: &Semantics<RootDatabase>,
403     callable: &hir::Callable,
404     param_name: &str,
405     argument: &ast::Expr,
406 ) -> bool {
407     // These are to be tested in the `parameter_hint_heuristics` test
408     // hide when:
409     // - the parameter name is a suffix of the function's name
410     // - the argument is an enum whose name is equal to the parameter
411     // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
412     //   of argument with _ splitting it off
413     // - param starts with `ra_fixture`
414     // - param is a well known name in a unary function
415
416     let param_name = param_name.trim_start_matches('_');
417     if param_name.is_empty() {
418         return true;
419     }
420
421     let fn_name = match callable.kind() {
422         hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
423         _ => None,
424     };
425     let fn_name = fn_name.as_deref();
426     is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
427         || is_enum_name_similar_to_param_name(sema, argument, param_name)
428         || is_argument_similar_to_param_name(argument, param_name)
429         || param_name.starts_with("ra_fixture")
430         || (callable.n_params() == 1 && is_obvious_param(param_name))
431 }
432
433 fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
434     // check whether param_name and argument are the same or
435     // whether param_name is a prefix/suffix of argument(split at `_`)
436     let argument = match get_string_representation(argument) {
437         Some(argument) => argument,
438         None => return false,
439     };
440
441     let param_name = param_name.trim_start_matches('_');
442     let argument = argument.trim_start_matches('_');
443     if argument.strip_prefix(param_name).map_or(false, |s| s.starts_with('_')) {
444         return true;
445     }
446     if argument.strip_suffix(param_name).map_or(false, |s| s.ends_with('_')) {
447         return true;
448     }
449     argument == param_name
450 }
451
452 /// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
453 ///
454 /// `fn strip_suffix(suffix)` will be hidden.
455 /// `fn stripsuffix(suffix)` will not be hidden.
456 fn is_param_name_suffix_of_fn_name(
457     param_name: &str,
458     callable: &Callable,
459     fn_name: Option<&str>,
460 ) -> bool {
461     match (callable.n_params(), fn_name) {
462         (1, Some(function)) => {
463             function == param_name
464                 || (function.len() > param_name.len()
465                     && function.ends_with(param_name)
466                     && function[..function.len() - param_name.len()].ends_with('_'))
467         }
468         _ => false,
469     }
470 }
471
472 fn is_enum_name_similar_to_param_name(
473     sema: &Semantics<RootDatabase>,
474     argument: &ast::Expr,
475     param_name: &str,
476 ) -> bool {
477     match sema.type_of_expr(argument).and_then(|t| t.original.as_adt()) {
478         Some(hir::Adt::Enum(e)) => to_lower_snake_case(&e.name(sema.db).to_string()) == param_name,
479         _ => false,
480     }
481 }
482
483 fn get_string_representation(expr: &ast::Expr) -> Option<String> {
484     match expr {
485         ast::Expr::MethodCallExpr(method_call_expr) => {
486             let name_ref = method_call_expr.name_ref()?;
487             match name_ref.text().as_str() {
488                 "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
489                 name_ref => Some(name_ref.to_owned()),
490             }
491         }
492         ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
493         ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
494         ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
495         ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
496         _ => None,
497     }
498 }
499
500 fn is_obvious_param(param_name: &str) -> bool {
501     // avoid displaying hints for common functions like map, filter, etc.
502     // or other obvious words used in std
503     let is_obvious_param_name =
504         matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
505     param_name.len() == 1 || is_obvious_param_name
506 }
507
508 fn get_callable(
509     sema: &Semantics<RootDatabase>,
510     expr: &ast::Expr,
511 ) -> Option<(hir::Callable, ast::ArgList)> {
512     match expr {
513         ast::Expr::CallExpr(expr) => {
514             let descended = sema.descend_node_into_attributes(expr.clone()).pop();
515             let expr = descended.as_ref().unwrap_or(expr);
516             sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
517         }
518         ast::Expr::MethodCallExpr(expr) => {
519             let descended = sema.descend_node_into_attributes(expr.clone()).pop();
520             let expr = descended.as_ref().unwrap_or(expr);
521             sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
522         }
523         _ => None,
524     }
525 }
526
527 #[cfg(test)]
528 mod tests {
529     use expect_test::{expect, Expect};
530     use test_utils::extract_annotations;
531
532     use crate::{fixture, inlay_hints::InlayHintsConfig};
533
534     const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
535         type_hints: true,
536         parameter_hints: true,
537         chaining_hints: true,
538         max_length: None,
539     };
540
541     #[track_caller]
542     fn check(ra_fixture: &str) {
543         check_with_config(TEST_CONFIG, ra_fixture);
544     }
545
546     #[track_caller]
547     fn check_params(ra_fixture: &str) {
548         check_with_config(
549             InlayHintsConfig {
550                 parameter_hints: true,
551                 type_hints: false,
552                 chaining_hints: false,
553                 max_length: None,
554             },
555             ra_fixture,
556         );
557     }
558
559     #[track_caller]
560     fn check_types(ra_fixture: &str) {
561         check_with_config(
562             InlayHintsConfig {
563                 parameter_hints: false,
564                 type_hints: true,
565                 chaining_hints: false,
566                 max_length: None,
567             },
568             ra_fixture,
569         );
570     }
571
572     #[track_caller]
573     fn check_chains(ra_fixture: &str) {
574         check_with_config(
575             InlayHintsConfig {
576                 parameter_hints: false,
577                 type_hints: false,
578                 chaining_hints: true,
579                 max_length: None,
580             },
581             ra_fixture,
582         );
583     }
584
585     #[track_caller]
586     fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
587         let (analysis, file_id) = fixture::file(ra_fixture);
588         let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
589         let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
590         let actual =
591             inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
592         assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
593     }
594
595     #[track_caller]
596     fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
597         let (analysis, file_id) = fixture::file(ra_fixture);
598         let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
599         expect.assert_debug_eq(&inlay_hints)
600     }
601
602     #[test]
603     fn hints_disabled() {
604         check_with_config(
605             InlayHintsConfig {
606                 type_hints: false,
607                 parameter_hints: false,
608                 chaining_hints: false,
609                 max_length: None,
610             },
611             r#"
612 fn foo(a: i32, b: i32) -> i32 { a + b }
613 fn main() {
614     let _x = foo(4, 4);
615 }"#,
616         );
617     }
618
619     // Parameter hint tests
620
621     #[test]
622     fn param_hints_only() {
623         check_params(
624             r#"
625 fn foo(a: i32, b: i32) -> i32 { a + b }
626 fn main() {
627     let _x = foo(
628         4,
629       //^ a
630         4,
631       //^ b
632     );
633 }"#,
634         );
635     }
636
637     #[test]
638     fn param_name_similar_to_fn_name_still_hints() {
639         check_params(
640             r#"
641 fn max(x: i32, y: i32) -> i32 { x + y }
642 fn main() {
643     let _x = max(
644         4,
645       //^ x
646         4,
647       //^ y
648     );
649 }"#,
650         );
651     }
652
653     #[test]
654     fn param_name_similar_to_fn_name() {
655         check_params(
656             r#"
657 fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
658 fn main() {
659     let _x = param_with_underscore(
660         4,
661     );
662 }"#,
663         );
664         check_params(
665             r#"
666 fn param_with_underscore(underscore: i32) -> i32 { underscore }
667 fn main() {
668     let _x = param_with_underscore(
669         4,
670     );
671 }"#,
672         );
673     }
674
675     #[test]
676     fn param_name_same_as_fn_name() {
677         check_params(
678             r#"
679 fn foo(foo: i32) -> i32 { foo }
680 fn main() {
681     let _x = foo(
682         4,
683     );
684 }"#,
685         );
686     }
687
688     #[test]
689     fn never_hide_param_when_multiple_params() {
690         check_params(
691             r#"
692 fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
693 fn main() {
694     let _x = foo(
695         4,
696       //^ foo
697         8,
698       //^ bar
699     );
700 }"#,
701         );
702     }
703
704     #[test]
705     fn param_hints_look_through_as_ref_and_clone() {
706         check_params(
707             r#"
708 fn foo(bar: i32, baz: f32) {}
709
710 fn main() {
711     let bar = 3;
712     let baz = &"baz";
713     let fez = 1.0;
714     foo(bar.clone(), bar.clone());
715                    //^^^^^^^^^^^ baz
716     foo(bar.as_ref(), bar.as_ref());
717                     //^^^^^^^^^^^^ baz
718 }
719 "#,
720         );
721     }
722
723     #[test]
724     fn self_param_hints() {
725         check_params(
726             r#"
727 struct Foo;
728
729 impl Foo {
730     fn foo(self: Self) {}
731     fn bar(self: &Self) {}
732 }
733
734 fn main() {
735     Foo::foo(Foo);
736            //^^^ self
737     Foo::bar(&Foo);
738            //^^^^ self
739 }
740 "#,
741         )
742     }
743
744     #[test]
745     fn param_name_hints_show_for_literals() {
746         check_params(
747             r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
748 fn main() {
749     test(
750         0xa_b,
751       //^^^^^ a
752         0xa_b,
753       //^^^^^ b
754     );
755 }"#,
756         )
757     }
758
759     #[test]
760     fn function_call_parameter_hint() {
761         check_params(
762             r#"
763 //- minicore: option
764 struct FileId {}
765 struct SmolStr {}
766
767 struct TextRange {}
768 struct SyntaxKind {}
769 struct NavigationTarget {}
770
771 struct Test {}
772
773 impl Test {
774     fn method(&self, mut param: i32) -> i32 { param * 2 }
775
776     fn from_syntax(
777         file_id: FileId,
778         name: SmolStr,
779         focus_range: Option<TextRange>,
780         full_range: TextRange,
781         kind: SyntaxKind,
782         docs: Option<String>,
783     ) -> NavigationTarget {
784         NavigationTarget {}
785     }
786 }
787
788 fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
789     foo + bar
790 }
791
792 fn main() {
793     let not_literal = 1;
794     let _: i32 = test_func(1,    2,      "hello", 3,  not_literal);
795                          //^ foo ^ bar   ^^^^^^^ msg  ^^^^^^^^^^^ last
796     let t: Test = Test {};
797     t.method(123);
798            //^^^ param
799     Test::method(&t,      3456);
800                //^^ self  ^^^^ param
801     Test::from_syntax(
802         FileId {},
803       //^^^^^^^^^ file_id
804         "impl".into(),
805       //^^^^^^^^^^^^^ name
806         None,
807       //^^^^ focus_range
808         TextRange {},
809       //^^^^^^^^^^^^ full_range
810         SyntaxKind {},
811       //^^^^^^^^^^^^^ kind
812         None,
813       //^^^^ docs
814     );
815 }"#,
816         );
817     }
818
819     #[test]
820     fn parameter_hint_heuristics() {
821         check_params(
822             r#"
823 fn check(ra_fixture_thing: &str) {}
824
825 fn map(f: i32) {}
826 fn filter(predicate: i32) {}
827
828 fn strip_suffix(suffix: &str) {}
829 fn stripsuffix(suffix: &str) {}
830 fn same(same: u32) {}
831 fn same2(_same2: u32) {}
832
833 fn enum_matches_param_name(completion_kind: CompletionKind) {}
834
835 fn foo(param: u32) {}
836 fn bar(param_eter: u32) {}
837
838 enum CompletionKind {
839     Keyword,
840 }
841
842 fn non_ident_pat((a, b): (u32, u32)) {}
843
844 fn main() {
845     check("");
846
847     map(0);
848     filter(0);
849
850     strip_suffix("");
851     stripsuffix("");
852               //^^ suffix
853     same(0);
854     same2(0);
855
856     enum_matches_param_name(CompletionKind::Keyword);
857
858     let param = 0;
859     foo(param);
860     let param_end = 0;
861     foo(param_end);
862     let start_param = 0;
863     foo(start_param);
864     let param2 = 0;
865     foo(param2);
866       //^^^^^^ param
867
868     let param_eter = 0;
869     bar(param_eter);
870     let param_eter_end = 0;
871     bar(param_eter_end);
872     let start_param_eter = 0;
873     bar(start_param_eter);
874     let param_eter2 = 0;
875     bar(param_eter2);
876       //^^^^^^^^^^^ param_eter
877
878     non_ident_pat((0, 0));
879 }"#,
880         );
881     }
882
883     // Type-Hint tests
884
885     #[test]
886     fn type_hints_only() {
887         check_types(
888             r#"
889 fn foo(a: i32, b: i32) -> i32 { a + b }
890 fn main() {
891     let _x = foo(4, 4);
892       //^^ i32
893 }"#,
894         );
895     }
896
897     #[test]
898     fn type_hints_bindings_after_at() {
899         check_types(
900             r#"
901 //- minicore: option
902 fn main() {
903     let ref foo @ bar @ ref mut baz = 0;
904           //^^^ &i32
905                 //^^^ i32
906                               //^^^ &mut i32
907     let [x @ ..] = [0];
908        //^ [i32; 1]
909     if let x @ Some(_) = Some(0) {}
910          //^ Option<i32>
911     let foo @ (bar, baz) = (3, 3);
912       //^^^ (i32, i32)
913              //^^^ i32
914                   //^^^ i32
915 }"#,
916         );
917     }
918
919     #[test]
920     fn default_generic_types_should_not_be_displayed() {
921         check(
922             r#"
923 struct Test<K, T = u8> { k: K, t: T }
924
925 fn main() {
926     let zz = Test { t: 23u8, k: 33 };
927       //^^ Test<i32>
928     let zz_ref = &zz;
929       //^^^^^^ &Test<i32>
930     let test = || zz;
931       //^^^^ || -> Test<i32>
932 }"#,
933         );
934     }
935
936     #[test]
937     fn shorten_iterators_in_associated_params() {
938         check_types(
939             r#"
940 //- minicore: iterators
941 use core::iter;
942
943 pub struct SomeIter<T> {}
944
945 impl<T> SomeIter<T> {
946     pub fn new() -> Self { SomeIter {} }
947     pub fn push(&mut self, t: T) {}
948 }
949
950 impl<T> Iterator for SomeIter<T> {
951     type Item = T;
952     fn next(&mut self) -> Option<Self::Item> {
953         None
954     }
955 }
956
957 fn main() {
958     let mut some_iter = SomeIter::new();
959           //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
960       some_iter.push(iter::repeat(2).take(2));
961     let iter_of_iters = some_iter.take(2);
962       //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
963 }
964 "#,
965         );
966     }
967
968     #[test]
969     fn infer_call_method_return_associated_types_with_generic() {
970         check_types(
971             r#"
972             pub trait Default {
973                 fn default() -> Self;
974             }
975             pub trait Foo {
976                 type Bar: Default;
977             }
978
979             pub fn quux<T: Foo>() -> T::Bar {
980                 let y = Default::default();
981                   //^ <T as Foo>::Bar
982
983                 y
984             }
985             "#,
986         );
987     }
988
989     #[test]
990     fn fn_hints() {
991         check_types(
992             r#"
993 //- minicore: fn, sized
994 fn foo() -> impl Fn() { loop {} }
995 fn foo1() -> impl Fn(f64) { loop {} }
996 fn foo2() -> impl Fn(f64, f64) { loop {} }
997 fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
998 fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
999 fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1000 fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1001 fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1002
1003 fn main() {
1004     let foo = foo();
1005      // ^^^ impl Fn()
1006     let foo = foo1();
1007      // ^^^ impl Fn(f64)
1008     let foo = foo2();
1009      // ^^^ impl Fn(f64, f64)
1010     let foo = foo3();
1011      // ^^^ impl Fn(f64, f64) -> u32
1012     let foo = foo4();
1013      // ^^^ &dyn Fn(f64, f64) -> u32
1014     let foo = foo5();
1015      // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1016     let foo = foo6();
1017      // ^^^ impl Fn(f64, f64) -> u32
1018     let foo = foo7();
1019      // ^^^ *const impl Fn(f64, f64) -> u32
1020 }
1021 "#,
1022         )
1023     }
1024
1025     #[test]
1026     fn fn_hints_ptr_rpit_fn_parentheses() {
1027         check_types(
1028             r#"
1029 //- minicore: fn, sized
1030 trait Trait {}
1031
1032 fn foo1() -> *const impl Fn() { loop {} }
1033 fn foo2() -> *const (impl Fn() + Sized) { loop {} }
1034 fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
1035 fn foo4() -> *const (impl Sized + Fn()) { loop {} }
1036 fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
1037 fn foo6() -> *const (impl Fn() + Trait) { loop {} }
1038 fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
1039 fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
1040 fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
1041 fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
1042
1043 fn main() {
1044     let foo = foo1();
1045     //  ^^^ *const impl Fn()
1046     let foo = foo2();
1047     //  ^^^ *const impl Fn()
1048     let foo = foo3();
1049     //  ^^^ *const (impl Fn() + ?Sized)
1050     let foo = foo4();
1051     //  ^^^ *const impl Fn()
1052     let foo = foo5();
1053     //  ^^^ *const (impl Fn() + ?Sized)
1054     let foo = foo6();
1055     //  ^^^ *const (impl Fn() + Trait)
1056     let foo = foo7();
1057     //  ^^^ *const (impl Fn() + Trait)
1058     let foo = foo8();
1059     //  ^^^ *const (impl Fn() + Trait + ?Sized)
1060     let foo = foo9();
1061     //  ^^^ *const (impl Fn() -> u8 + ?Sized)
1062     let foo = foo10();
1063     //  ^^^ *const impl Fn()
1064 }
1065 "#,
1066         )
1067     }
1068
1069     #[test]
1070     fn unit_structs_have_no_type_hints() {
1071         check_types(
1072             r#"
1073 //- minicore: result
1074 struct SyntheticSyntax;
1075
1076 fn main() {
1077     match Ok(()) {
1078         Ok(_) => (),
1079         Err(SyntheticSyntax) => (),
1080     }
1081 }"#,
1082         );
1083     }
1084
1085     #[test]
1086     fn let_statement() {
1087         check_types(
1088             r#"
1089 #[derive(PartialEq)]
1090 enum Option<T> { None, Some(T) }
1091
1092 #[derive(PartialEq)]
1093 struct Test { a: Option<u32>, b: u8 }
1094
1095 fn main() {
1096     struct InnerStruct {}
1097
1098     let test = 54;
1099       //^^^^ i32
1100     let test: i32 = 33;
1101     let mut test = 33;
1102           //^^^^ i32
1103     let _ = 22;
1104     let test = "test";
1105       //^^^^ &str
1106     let test = InnerStruct {};
1107       //^^^^ InnerStruct
1108
1109     let test = unresolved();
1110
1111     let test = (42, 'a');
1112       //^^^^ (i32, char)
1113     let (a,    (b,     (c,)) = (2, (3, (9.2,));
1114        //^ i32  ^ i32   ^ f64
1115     let &x = &92;
1116        //^ i32
1117 }"#,
1118         );
1119     }
1120
1121     #[test]
1122     fn if_expr() {
1123         check_types(
1124             r#"
1125 //- minicore: option
1126 struct Test { a: Option<u32>, b: u8 }
1127
1128 fn main() {
1129     let test = Some(Test { a: Some(3), b: 1 });
1130       //^^^^ Option<Test>
1131     if let None = &test {};
1132     if let test = &test {};
1133          //^^^^ &Option<Test>
1134     if let Some(test) = &test {};
1135               //^^^^ &Test
1136     if let Some(Test { a,             b }) = &test {};
1137                      //^ &Option<u32> ^ &u8
1138     if let Some(Test { a: x,             b: y }) = &test {};
1139                         //^ &Option<u32>    ^ &u8
1140     if let Some(Test { a: Some(x),  b: y }) = &test {};
1141                              //^ &u32  ^ &u8
1142     if let Some(Test { a: None,  b: y }) = &test {};
1143                                   //^ &u8
1144     if let Some(Test { b: y, .. }) = &test {};
1145                         //^ &u8
1146     if test == None {}
1147 }"#,
1148         );
1149     }
1150
1151     #[test]
1152     fn while_expr() {
1153         check_types(
1154             r#"
1155 //- minicore: option
1156 struct Test { a: Option<u32>, b: u8 }
1157
1158 fn main() {
1159     let test = Some(Test { a: Some(3), b: 1 });
1160       //^^^^ Option<Test>
1161     while let Some(Test { a: Some(x),  b: y }) = &test {};
1162                                 //^ &u32  ^ &u8
1163 }"#,
1164         );
1165     }
1166
1167     #[test]
1168     fn match_arm_list() {
1169         check_types(
1170             r#"
1171 //- minicore: option
1172 struct Test { a: Option<u32>, b: u8 }
1173
1174 fn main() {
1175     match Some(Test { a: Some(3), b: 1 }) {
1176         None => (),
1177         test => (),
1178       //^^^^ Option<Test>
1179         Some(Test { a: Some(x), b: y }) => (),
1180                           //^ u32  ^ u8
1181         _ => {}
1182     }
1183 }"#,
1184         );
1185     }
1186
1187     #[test]
1188     fn incomplete_for_no_hint() {
1189         check_types(
1190             r#"
1191 fn main() {
1192     let data = &[1i32, 2, 3];
1193       //^^^^ &[i32; 3]
1194     for i
1195 }"#,
1196         );
1197         check(
1198             r#"
1199 pub struct Vec<T> {}
1200
1201 impl<T> Vec<T> {
1202     pub fn new() -> Self { Vec {} }
1203     pub fn push(&mut self, t: T) {}
1204 }
1205
1206 impl<T> IntoIterator for Vec<T> {
1207     type Item=T;
1208 }
1209
1210 fn main() {
1211     let mut data = Vec::new();
1212           //^^^^ Vec<&str>
1213     data.push("foo");
1214     for i in
1215
1216     println!("Unit expr");
1217 }
1218 "#,
1219         );
1220     }
1221
1222     #[test]
1223     fn complete_for_hint() {
1224         check_types(
1225             r#"
1226 //- minicore: iterator
1227 pub struct Vec<T> {}
1228
1229 impl<T> Vec<T> {
1230     pub fn new() -> Self { Vec {} }
1231     pub fn push(&mut self, t: T) {}
1232 }
1233
1234 impl<T> IntoIterator for Vec<T> {
1235     type Item=T;
1236 }
1237
1238 fn main() {
1239     let mut data = Vec::new();
1240           //^^^^ Vec<&str>
1241     data.push("foo");
1242     for i in data {
1243       //^ &str
1244       let z = i;
1245         //^ &str
1246     }
1247 }
1248 "#,
1249         );
1250     }
1251
1252     #[test]
1253     fn multi_dyn_trait_bounds() {
1254         check_types(
1255             r#"
1256 pub struct Vec<T> {}
1257
1258 impl<T> Vec<T> {
1259     pub fn new() -> Self { Vec {} }
1260 }
1261
1262 pub struct Box<T> {}
1263
1264 trait Display {}
1265 trait Sync {}
1266
1267 fn main() {
1268     // The block expression wrapping disables the constructor hint hiding logic
1269     let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
1270       //^^ Vec<Box<&(dyn Display + Sync)>>
1271     let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
1272       //^^ Vec<Box<*const (dyn Display + Sync)>>
1273     let _v = { Vec::<Box<dyn Display + Sync>>::new() };
1274       //^^ Vec<Box<dyn Display + Sync>>
1275 }
1276 "#,
1277         );
1278     }
1279
1280     #[test]
1281     fn shorten_iterator_hints() {
1282         check_types(
1283             r#"
1284 //- minicore: iterators
1285 use core::iter;
1286
1287 struct MyIter;
1288
1289 impl Iterator for MyIter {
1290     type Item = ();
1291     fn next(&mut self) -> Option<Self::Item> {
1292         None
1293     }
1294 }
1295
1296 fn main() {
1297     let _x = MyIter;
1298       //^^ MyIter
1299     let _x = iter::repeat(0);
1300       //^^ impl Iterator<Item = i32>
1301     fn generic<T: Clone>(t: T) {
1302         let _x = iter::repeat(t);
1303           //^^ impl Iterator<Item = T>
1304         let _chained = iter::repeat(t).take(10);
1305           //^^^^^^^^ impl Iterator<Item = T>
1306     }
1307 }
1308 "#,
1309         );
1310     }
1311
1312     #[test]
1313     fn skip_constructor_type_hints() {
1314         check_types(
1315             r#"
1316 //- minicore: try
1317 use core::ops::ControlFlow;
1318
1319 struct Struct;
1320 struct TupleStruct();
1321
1322 impl Struct {
1323     fn new() -> Self {
1324         Struct
1325     }
1326     fn try_new() -> ControlFlow<(), Self> {
1327         ControlFlow::Continue(Struct)
1328     }
1329 }
1330
1331 struct Generic<T>(T);
1332 impl Generic<i32> {
1333     fn new() -> Self {
1334         Generic(0)
1335     }
1336 }
1337
1338 fn main() {
1339     let strukt = Struct::new();
1340     let tuple_struct = TupleStruct();
1341     let generic0 = Generic::new();
1342      // ^^^^^^^^ Generic<i32>
1343     let generic1 = Generic::<i32>::new();
1344     let generic2 = <Generic<i32>>::new();
1345 }
1346
1347 fn fallible() -> ControlFlow<()> {
1348     let strukt = Struct::try_new()?;
1349 }
1350 "#,
1351         );
1352     }
1353
1354     #[test]
1355     fn closures() {
1356         check(
1357             r#"
1358 fn main() {
1359     let mut start = 0;
1360           //^^^^^ i32
1361     (0..2).for_each(|increment| { start += increment; });
1362                    //^^^^^^^^^ i32
1363
1364     let multiply =
1365       //^^^^^^^^ |…| -> i32
1366       | a,     b| a * b
1367       //^ i32  ^ i32
1368     ;
1369
1370     let _: i32 = multiply(1, 2);
1371     let multiply_ref = &multiply;
1372       //^^^^^^^^^^^^ &|…| -> i32
1373
1374     let return_42 = || 42;
1375       //^^^^^^^^^ || -> i32
1376 }"#,
1377         );
1378     }
1379
1380     #[test]
1381     fn hint_truncation() {
1382         check_with_config(
1383             InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
1384             r#"
1385 struct Smol<T>(T);
1386
1387 struct VeryLongOuterName<T>(T);
1388
1389 fn main() {
1390     let a = Smol(0u32);
1391       //^ Smol<u32>
1392     let b = VeryLongOuterName(0usize);
1393       //^ VeryLongOuterName<…>
1394     let c = Smol(Smol(0u32))
1395       //^ Smol<Smol<…>>
1396 }"#,
1397         );
1398     }
1399
1400     // Chaining hint tests
1401
1402     #[test]
1403     fn chaining_hints_ignore_comments() {
1404         check_expect(
1405             InlayHintsConfig {
1406                 parameter_hints: false,
1407                 type_hints: false,
1408                 chaining_hints: true,
1409                 max_length: None,
1410             },
1411             r#"
1412 struct A(B);
1413 impl A { fn into_b(self) -> B { self.0 } }
1414 struct B(C);
1415 impl B { fn into_c(self) -> C { self.0 } }
1416 struct C;
1417
1418 fn main() {
1419     let c = A(B(C))
1420         .into_b() // This is a comment
1421         // This is another comment
1422         .into_c();
1423 }
1424 "#,
1425             expect![[r#"
1426                 [
1427                     InlayHint {
1428                         range: 147..172,
1429                         kind: ChainingHint,
1430                         label: "B",
1431                     },
1432                     InlayHint {
1433                         range: 147..154,
1434                         kind: ChainingHint,
1435                         label: "A",
1436                     },
1437                 ]
1438             "#]],
1439         );
1440     }
1441
1442     #[test]
1443     fn chaining_hints_without_newlines() {
1444         check_chains(
1445             r#"
1446 struct A(B);
1447 impl A { fn into_b(self) -> B { self.0 } }
1448 struct B(C);
1449 impl B { fn into_c(self) -> C { self.0 } }
1450 struct C;
1451
1452 fn main() {
1453     let c = A(B(C)).into_b().into_c();
1454 }"#,
1455         );
1456     }
1457
1458     #[test]
1459     fn struct_access_chaining_hints() {
1460         check_expect(
1461             InlayHintsConfig {
1462                 parameter_hints: false,
1463                 type_hints: false,
1464                 chaining_hints: true,
1465                 max_length: None,
1466             },
1467             r#"
1468 struct A { pub b: B }
1469 struct B { pub c: C }
1470 struct C(pub bool);
1471 struct D;
1472
1473 impl D {
1474     fn foo(&self) -> i32 { 42 }
1475 }
1476
1477 fn main() {
1478     let x = A { b: B { c: C(true) } }
1479         .b
1480         .c
1481         .0;
1482     let x = D
1483         .foo();
1484 }"#,
1485             expect![[r#"
1486                 [
1487                     InlayHint {
1488                         range: 143..190,
1489                         kind: ChainingHint,
1490                         label: "C",
1491                     },
1492                     InlayHint {
1493                         range: 143..179,
1494                         kind: ChainingHint,
1495                         label: "B",
1496                     },
1497                 ]
1498             "#]],
1499         );
1500     }
1501
1502     #[test]
1503     fn generic_chaining_hints() {
1504         check_expect(
1505             InlayHintsConfig {
1506                 parameter_hints: false,
1507                 type_hints: false,
1508                 chaining_hints: true,
1509                 max_length: None,
1510             },
1511             r#"
1512 struct A<T>(T);
1513 struct B<T>(T);
1514 struct C<T>(T);
1515 struct X<T,R>(T, R);
1516
1517 impl<T> A<T> {
1518     fn new(t: T) -> Self { A(t) }
1519     fn into_b(self) -> B<T> { B(self.0) }
1520 }
1521 impl<T> B<T> {
1522     fn into_c(self) -> C<T> { C(self.0) }
1523 }
1524 fn main() {
1525     let c = A::new(X(42, true))
1526         .into_b()
1527         .into_c();
1528 }
1529 "#,
1530             expect![[r#"
1531                 [
1532                     InlayHint {
1533                         range: 246..283,
1534                         kind: ChainingHint,
1535                         label: "B<X<i32, bool>>",
1536                     },
1537                     InlayHint {
1538                         range: 246..265,
1539                         kind: ChainingHint,
1540                         label: "A<X<i32, bool>>",
1541                     },
1542                 ]
1543             "#]],
1544         );
1545     }
1546
1547     #[test]
1548     fn shorten_iterator_chaining_hints() {
1549         check_expect(
1550             InlayHintsConfig {
1551                 parameter_hints: false,
1552                 type_hints: false,
1553                 chaining_hints: true,
1554                 max_length: None,
1555             },
1556             r#"
1557 //- minicore: iterators
1558 use core::iter;
1559
1560 struct MyIter;
1561
1562 impl Iterator for MyIter {
1563     type Item = ();
1564     fn next(&mut self) -> Option<Self::Item> {
1565         None
1566     }
1567 }
1568
1569 fn main() {
1570     let _x = MyIter.by_ref()
1571         .take(5)
1572         .by_ref()
1573         .take(5)
1574         .by_ref();
1575 }
1576 "#,
1577             expect![[r#"
1578                 [
1579                     InlayHint {
1580                         range: 174..241,
1581                         kind: ChainingHint,
1582                         label: "impl Iterator<Item = ()>",
1583                     },
1584                     InlayHint {
1585                         range: 174..224,
1586                         kind: ChainingHint,
1587                         label: "impl Iterator<Item = ()>",
1588                     },
1589                     InlayHint {
1590                         range: 174..206,
1591                         kind: ChainingHint,
1592                         label: "impl Iterator<Item = ()>",
1593                     },
1594                     InlayHint {
1595                         range: 174..189,
1596                         kind: ChainingHint,
1597                         label: "&mut MyIter",
1598                     },
1599                 ]
1600             "#]],
1601         );
1602     }
1603
1604     #[test]
1605     fn hints_in_attr_call() {
1606         check_expect(
1607             TEST_CONFIG,
1608             r#"
1609 //- proc_macros: identity, input_replace
1610 struct Struct;
1611 impl Struct {
1612     fn chain(self) -> Self {
1613         self
1614     }
1615 }
1616 #[proc_macros::identity]
1617 fn main() {
1618     let strukt = Struct;
1619     strukt
1620         .chain()
1621         .chain()
1622         .chain();
1623     Struct::chain(strukt);
1624 }
1625 "#,
1626             expect![[r#"
1627                 [
1628                     InlayHint {
1629                         range: 124..130,
1630                         kind: TypeHint,
1631                         label: "Struct",
1632                     },
1633                     InlayHint {
1634                         range: 145..185,
1635                         kind: ChainingHint,
1636                         label: "Struct",
1637                     },
1638                     InlayHint {
1639                         range: 145..168,
1640                         kind: ChainingHint,
1641                         label: "Struct",
1642                     },
1643                     InlayHint {
1644                         range: 222..228,
1645                         kind: ParameterHint,
1646                         label: "self",
1647                     },
1648                 ]
1649             "#]],
1650         );
1651     }
1652 }