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