2 use hir::{known, Callable, HasVisibility, HirDisplay, Semantics, TypeInfo};
3 use ide_db::{base_db::FileRange, helpers::FamousDefs, RootDatabase};
4 use itertools::Itertools;
5 use stdx::to_lower_snake_case;
7 ast::{self, AstNode, HasArgList, HasName, UnaryOp},
8 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
13 #[derive(Clone, Debug, PartialEq, Eq)]
14 pub struct InlayHintsConfig {
16 pub parameter_hints: bool,
17 pub chaining_hints: bool,
18 pub hide_named_constructor_hints: bool,
19 pub max_length: Option<usize>,
22 #[derive(Clone, Debug, PartialEq, Eq)]
30 pub struct InlayHint {
36 // Feature: Inlay Hints
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.
41 // rust-analyzer shows hints for
43 // * types of local variables
44 // * names of function arguments
45 // * types of chained expressions
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].
52 // | Editor | Action Name
54 // | VS Code | **Rust Analyzer: Toggle inlay hints*
57 // image::https://user-images.githubusercontent.com/48062697/113020660-b5f98b80-917a-11eb-8d70-3be3fd558cdd.png[]
58 pub(crate) fn inlay_hints(
61 config: &InlayHintsConfig,
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();
68 let mut res = Vec::new();
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);
74 ast::Expr::CallExpr(it) => {
75 get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
77 ast::Expr::MethodCallExpr(it) => {
78 get_param_name_hints(&mut res, &sema, config, ast::Expr::from(it));
82 } else if let Some(it) = ast::IdentPat::cast(node.clone()) {
83 get_bind_pat_hints(&mut res, &sema, config, &it);
89 fn get_chaining_hints(
90 acc: &mut Vec<InlayHint>,
91 sema: &Semantics<RootDatabase>,
92 config: &InlayHintsConfig,
95 if !config.chaining_hints {
99 if matches!(expr, ast::Expr::RecordExpr(_)) {
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);
108 let mut tokens = expr
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,
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();
126 if next_next == T![.] {
127 let ty = sema.type_of_expr(desc_expr)?.original;
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() {
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()
150 fn get_param_name_hints(
151 acc: &mut Vec<InlayHint>,
152 sema: &Semantics<RootDatabase>,
153 config: &InlayHintsConfig,
156 if !config.parameter_hints {
160 let (callable, arg_list) = get_callable(sema, &expr)?;
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(),
175 Some((param_name, arg, range))
177 .filter(|(param_name, arg, _)| {
178 !should_hide_param_name_hint(sema, &callable, param_name, arg)
180 .map(|(param_name, _, FileRange { range, .. })| InlayHint {
182 kind: InlayKind::ParameterHint,
183 label: param_name.into(),
190 fn get_bind_pat_hints(
191 acc: &mut Vec<InlayHint>,
192 sema: &Semantics<RootDatabase>,
193 config: &InlayHintsConfig,
196 if !config.type_hints {
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;
204 if should_not_display_type_hint(sema, pat, &ty) {
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);
212 let label = match label {
213 Some(label) => label,
215 let ty_name = ty.display_truncated(sema.db, config.max_length).to_string();
216 if config.hide_named_constructor_hints
217 && is_named_constructor(sema, pat, &ty_name).is_some()
226 range: match pat.name() {
227 Some(name) => name.syntax().text_range(),
228 None => pat.syntax().text_range(),
230 kind: InlayKind::TypeHint,
237 fn is_named_constructor(
238 sema: &Semantics<RootDatabase>,
242 let let_node = pat.syntax().parent()?;
243 let expr = match_ast! {
245 ast::LetStmt(it) => it.initializer(),
246 ast::LetExpr(it) => it.expr(),
251 let expr = sema.descend_node_into_attributes(expr.clone()).pop().unwrap_or(expr);
252 // unwrap postfix expressions
253 let expr = match expr {
254 ast::Expr::TryExpr(it) => it.expr(),
255 ast::Expr::AwaitExpr(it) => it.expr(),
258 let expr = match expr {
259 ast::Expr::CallExpr(call) => match call.expr()? {
260 ast::Expr::PathExpr(path) => path,
263 ast::Expr::PathExpr(path) => path,
266 let path = expr.path()?;
268 let callable = sema.type_of_expr(&ast::Expr::PathExpr(expr))?.original.as_callable(sema.db);
269 let callable_kind = callable.map(|it| it.kind());
270 let qual_seg = match callable_kind {
271 Some(hir::CallableKind::Function(_) | hir::CallableKind::TupleEnumVariant(_)) => {
272 path.qualifier()?.segment()
277 let ctor_name = match qual_seg.kind()? {
278 ast::PathSegmentKind::Name(name_ref) => {
279 match qual_seg.generic_arg_list().map(|it| it.generic_args()) {
280 Some(generics) => format!("{}<{}>", name_ref, generics.format(", ")),
281 None => name_ref.to_string(),
284 ast::PathSegmentKind::Type { type_ref: Some(ty), trait_ref: None } => ty.to_string(),
287 (ctor_name == ty_name).then(|| ())
290 /// Checks if the type is an Iterator from std::iter and replaces its hint with an `impl Iterator<Item = Ty>`.
292 sema: &Semantics<RootDatabase>,
293 famous_defs: &FamousDefs,
294 config: &InlayHintsConfig,
296 ) -> Option<SmolStr> {
298 let strukt = ty.strip_references().as_adt()?;
299 let krate = strukt.module(db).krate();
300 if krate != famous_defs.core()? {
303 let iter_trait = famous_defs.core_iter_Iterator()?;
304 let iter_mod = famous_defs.core_iter()?;
306 // Assert that this struct comes from `core::iter`.
307 if !(strukt.visibility(db) == hir::Visibility::Public
308 && strukt.module(db).path_to_root(db).contains(&iter_mod))
313 if ty.impls_trait(db, iter_trait, &[]) {
314 let assoc_type_item = iter_trait.items(db).into_iter().find_map(|item| match item {
315 hir::AssocItem::TypeAlias(alias) if alias.name(db) == known::Item => Some(alias),
318 if let Some(ty) = ty.normalize_trait_assoc_type(db, &[], assoc_type_item) {
319 const LABEL_START: &str = "impl Iterator<Item = ";
320 const LABEL_END: &str = ">";
322 let ty_display = hint_iterator(sema, famous_defs, config, &ty)
323 .map(|assoc_type_impl| assoc_type_impl.to_string())
325 ty.display_truncated(
329 .map(|len| len.saturating_sub(LABEL_START.len() + LABEL_END.len())),
333 return Some(format!("{}{}{}", LABEL_START, ty_display, LABEL_END).into());
340 fn pat_is_enum_variant(db: &RootDatabase, bind_pat: &ast::IdentPat, pat_ty: &hir::Type) -> bool {
341 if let Some(hir::Adt::Enum(enum_data)) = pat_ty.as_adt() {
342 let pat_text = bind_pat.to_string();
346 .map(|variant| variant.name(db).to_smol_str())
347 .any(|enum_name| enum_name == pat_text)
353 fn should_not_display_type_hint(
354 sema: &Semantics<RootDatabase>,
355 bind_pat: &ast::IdentPat,
360 if pat_ty.is_unknown() {
364 if let Some(hir::Adt::Struct(s)) = pat_ty.as_adt() {
365 if s.fields(db).is_empty() && s.name(db).to_smol_str() == bind_pat.to_string() {
370 for node in bind_pat.syntax().ancestors() {
373 ast::LetStmt(it) => return it.ty().is_some(),
374 ast::Param(it) => return it.ty().is_some(),
375 ast::MatchArm(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
376 ast::LetExpr(_) => return pat_is_enum_variant(db, bind_pat, pat_ty),
377 ast::IfExpr(_) => return false,
378 ast::WhileExpr(_) => return false,
379 ast::ForExpr(it) => {
380 // We *should* display hint only if user provided "in {expr}" and we know the type of expr (and it's not unit).
381 // Type of expr should be iterable.
382 return it.in_token().is_none() ||
384 .and_then(|iterable_expr| sema.type_of_expr(&iterable_expr))
385 .map(TypeInfo::original)
386 .map_or(true, |iterable_ty| iterable_ty.is_unknown() || iterable_ty.is_unit())
395 fn should_hide_param_name_hint(
396 sema: &Semantics<RootDatabase>,
397 callable: &hir::Callable,
399 argument: &ast::Expr,
401 // These are to be tested in the `parameter_hint_heuristics` test
403 // - the parameter name is a suffix of the function's name
404 // - the argument is an enum whose name is equal to the parameter
405 // - exact argument<->parameter match(ignoring leading underscore) or parameter is a prefix/suffix
406 // of argument with _ splitting it off
407 // - param starts with `ra_fixture`
408 // - param is a well known name in a unary function
410 let param_name = param_name.trim_start_matches('_');
411 if param_name.is_empty() {
415 if matches!(argument, ast::Expr::PrefixExpr(prefix) if prefix.op_kind() == Some(UnaryOp::Not)) {
419 let fn_name = match callable.kind() {
420 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_smol_str()),
423 let fn_name = fn_name.as_deref();
424 is_param_name_suffix_of_fn_name(param_name, callable, fn_name)
425 || is_enum_name_similar_to_param_name(sema, argument, param_name)
426 || is_argument_similar_to_param_name(argument, param_name)
427 || param_name.starts_with("ra_fixture")
428 || (callable.n_params() == 1 && is_obvious_param(param_name))
431 fn is_argument_similar_to_param_name(argument: &ast::Expr, param_name: &str) -> bool {
432 // check whether param_name and argument are the same or
433 // whether param_name is a prefix/suffix of argument(split at `_`)
434 let argument = match get_string_representation(argument) {
435 Some(argument) => argument,
436 None => return false,
439 // std is honestly too panic happy...
440 let str_split_at = |str: &str, at| str.is_char_boundary(at).then(|| argument.split_at(at));
442 let param_name = param_name.trim_start_matches('_');
443 let argument = argument.trim_start_matches('_');
445 match str_split_at(argument, param_name.len()) {
446 Some((prefix, rest)) if prefix.eq_ignore_ascii_case(param_name) => {
447 return rest.is_empty() || rest.starts_with('_');
451 match argument.len().checked_sub(param_name.len()).and_then(|at| str_split_at(argument, at)) {
452 Some((rest, suffix)) if param_name.eq_ignore_ascii_case(suffix) => {
453 return rest.is_empty() || rest.ends_with('_');
460 /// Hide the parameter name of a unary function if it is a `_` - prefixed suffix of the function's name, or equal.
462 /// `fn strip_suffix(suffix)` will be hidden.
463 /// `fn stripsuffix(suffix)` will not be hidden.
464 fn is_param_name_suffix_of_fn_name(
467 fn_name: Option<&str>,
469 match (callable.n_params(), fn_name) {
470 (1, Some(function)) => {
471 function == param_name
474 .checked_sub(param_name.len())
475 .and_then(|at| function.is_char_boundary(at).then(|| function.split_at(at)))
476 .map_or(false, |(prefix, suffix)| {
477 suffix.eq_ignore_ascii_case(param_name) && prefix.ends_with('_')
484 fn is_enum_name_similar_to_param_name(
485 sema: &Semantics<RootDatabase>,
486 argument: &ast::Expr,
489 match sema.type_of_expr(argument).and_then(|t| t.original.as_adt()) {
490 Some(hir::Adt::Enum(e)) => {
491 to_lower_snake_case(&e.name(sema.db).to_smol_str()) == param_name
497 fn get_string_representation(expr: &ast::Expr) -> Option<String> {
499 ast::Expr::MethodCallExpr(method_call_expr) => {
500 let name_ref = method_call_expr.name_ref()?;
501 match name_ref.text().as_str() {
502 "clone" | "as_ref" => method_call_expr.receiver().map(|rec| rec.to_string()),
503 name_ref => Some(name_ref.to_owned()),
506 ast::Expr::FieldExpr(field_expr) => Some(field_expr.name_ref()?.to_string()),
507 ast::Expr::PathExpr(path_expr) => Some(path_expr.path()?.segment()?.to_string()),
508 ast::Expr::PrefixExpr(prefix_expr) => get_string_representation(&prefix_expr.expr()?),
509 ast::Expr::RefExpr(ref_expr) => get_string_representation(&ref_expr.expr()?),
514 fn is_obvious_param(param_name: &str) -> bool {
515 // avoid displaying hints for common functions like map, filter, etc.
516 // or other obvious words used in std
517 let is_obvious_param_name =
518 matches!(param_name, "predicate" | "value" | "pat" | "rhs" | "other");
519 param_name.len() == 1 || is_obvious_param_name
523 sema: &Semantics<RootDatabase>,
525 ) -> Option<(hir::Callable, ast::ArgList)> {
527 ast::Expr::CallExpr(expr) => {
528 let descended = sema.descend_node_into_attributes(expr.clone()).pop();
529 let expr = descended.as_ref().unwrap_or(expr);
530 sema.type_of_expr(&expr.expr()?)?.original.as_callable(sema.db).zip(expr.arg_list())
532 ast::Expr::MethodCallExpr(expr) => {
533 let descended = sema.descend_node_into_attributes(expr.clone()).pop();
534 let expr = descended.as_ref().unwrap_or(expr);
535 sema.resolve_method_call_as_callable(expr).zip(expr.arg_list())
543 use expect_test::{expect, Expect};
544 use test_utils::extract_annotations;
546 use crate::{fixture, inlay_hints::InlayHintsConfig};
548 const TEST_CONFIG: InlayHintsConfig = InlayHintsConfig {
550 parameter_hints: true,
551 chaining_hints: true,
552 hide_named_constructor_hints: false,
557 fn check(ra_fixture: &str) {
558 check_with_config(TEST_CONFIG, ra_fixture);
562 fn check_params(ra_fixture: &str) {
565 parameter_hints: true,
567 chaining_hints: false,
568 hide_named_constructor_hints: false,
576 fn check_types(ra_fixture: &str) {
579 parameter_hints: false,
581 chaining_hints: false,
582 hide_named_constructor_hints: false,
590 fn check_chains(ra_fixture: &str) {
593 parameter_hints: false,
595 chaining_hints: true,
596 hide_named_constructor_hints: false,
604 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
605 let (analysis, file_id) = fixture::file(ra_fixture);
606 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
607 let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
609 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
610 assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
614 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
615 let (analysis, file_id) = fixture::file(ra_fixture);
616 let inlay_hints = analysis.inlay_hints(&config, file_id).unwrap();
617 expect.assert_debug_eq(&inlay_hints)
621 fn hints_disabled() {
625 parameter_hints: false,
626 chaining_hints: false,
627 hide_named_constructor_hints: false,
631 fn foo(a: i32, b: i32) -> i32 { a + b }
638 // Parameter hint tests
641 fn param_hints_only() {
644 fn foo(a: i32, b: i32) -> i32 { a + b }
657 fn param_name_similar_to_fn_name_still_hints() {
660 fn max(x: i32, y: i32) -> i32 { x + y }
673 fn param_name_similar_to_fn_name() {
676 fn param_with_underscore(with_underscore: i32) -> i32 { with_underscore }
678 let _x = param_with_underscore(
685 fn param_with_underscore(underscore: i32) -> i32 { underscore }
687 let _x = param_with_underscore(
695 fn param_name_same_as_fn_name() {
698 fn foo(foo: i32) -> i32 { foo }
708 fn never_hide_param_when_multiple_params() {
711 fn foo(foo: i32, bar: i32) -> i32 { bar + baz }
724 fn param_hints_look_through_as_ref_and_clone() {
727 fn foo(bar: i32, baz: f32) {}
733 foo(bar.clone(), bar.clone());
735 foo(bar.as_ref(), bar.as_ref());
743 fn self_param_hints() {
749 fn foo(self: Self) {}
750 fn bar(self: &Self) {}
764 fn param_name_hints_show_for_literals() {
766 r#"pub fn test(a: i32, b: i32) -> [i32; 2] { [a, b] }
779 fn function_call_parameter_hint() {
788 struct NavigationTarget {}
793 fn method(&self, mut param: i32) -> i32 { param * 2 }
798 focus_range: Option<TextRange>,
799 full_range: TextRange,
801 docs: Option<String>,
802 ) -> NavigationTarget {
807 fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
813 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
814 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
815 let t: Test = Test {};
818 Test::method(&t, 3456);
828 //^^^^^^^^^^^^ full_range
839 fn parameter_hint_heuristics() {
842 fn check(ra_fixture_thing: &str) {}
845 fn filter(predicate: i32) {}
847 fn strip_suffix(suffix: &str) {}
848 fn stripsuffix(suffix: &str) {}
849 fn same(same: u32) {}
850 fn same2(_same2: u32) {}
852 fn enum_matches_param_name(completion_kind: CompletionKind) {}
854 fn foo(param: u32) {}
855 fn bar(param_eter: u32) {}
857 enum CompletionKind {
861 fn non_ident_pat((a, b): (u32, u32)) {}
864 const PARAM: u32 = 0;
879 enum_matches_param_name(CompletionKind::Keyword);
893 let param_eter_end = 0;
895 let start_param_eter = 0;
896 bar(start_param_eter);
899 //^^^^^^^^^^^ param_eter
901 non_ident_pat((0, 0));
909 fn type_hints_only() {
912 fn foo(a: i32, b: i32) -> i32 { a + b }
921 fn type_hints_bindings_after_at() {
926 let ref foo @ bar @ ref mut baz = 0;
932 if let x @ Some(_) = Some(0) {}
934 let foo @ (bar, baz) = (3, 3);
943 fn default_generic_types_should_not_be_displayed() {
946 struct Test<K, T = u8> { k: K, t: T }
949 let zz = Test { t: 23u8, k: 33 };
954 //^^^^ || -> Test<i32>
960 fn shorten_iterators_in_associated_params() {
963 //- minicore: iterators
966 pub struct SomeIter<T> {}
968 impl<T> SomeIter<T> {
969 pub fn new() -> Self { SomeIter {} }
970 pub fn push(&mut self, t: T) {}
973 impl<T> Iterator for SomeIter<T> {
975 fn next(&mut self) -> Option<Self::Item> {
981 let mut some_iter = SomeIter::new();
982 //^^^^^^^^^ SomeIter<Take<Repeat<i32>>>
983 some_iter.push(iter::repeat(2).take(2));
984 let iter_of_iters = some_iter.take(2);
985 //^^^^^^^^^^^^^ impl Iterator<Item = impl Iterator<Item = i32>>
992 fn infer_call_method_return_associated_types_with_generic() {
996 fn default() -> Self;
1002 pub fn quux<T: Foo>() -> T::Bar {
1003 let y = Default::default();
1016 //- minicore: fn, sized
1017 fn foo() -> impl Fn() { loop {} }
1018 fn foo1() -> impl Fn(f64) { loop {} }
1019 fn foo2() -> impl Fn(f64, f64) { loop {} }
1020 fn foo3() -> impl Fn(f64, f64) -> u32 { loop {} }
1021 fn foo4() -> &'static dyn Fn(f64, f64) -> u32 { loop {} }
1022 fn foo5() -> &'static dyn Fn(&'static dyn Fn(f64, f64) -> u32, f64) -> u32 { loop {} }
1023 fn foo6() -> impl Fn(f64, f64) -> u32 + Sized { loop {} }
1024 fn foo7() -> *const (impl Fn(f64, f64) -> u32 + Sized) { loop {} }
1032 // ^^^ impl Fn(f64, f64)
1034 // ^^^ impl Fn(f64, f64) -> u32
1036 // ^^^ &dyn Fn(f64, f64) -> u32
1038 // ^^^ &dyn Fn(&dyn Fn(f64, f64) -> u32, f64) -> u32
1040 // ^^^ impl Fn(f64, f64) -> u32
1042 // ^^^ *const impl Fn(f64, f64) -> u32
1049 fn fn_hints_ptr_rpit_fn_parentheses() {
1052 //- minicore: fn, sized
1055 fn foo1() -> *const impl Fn() { loop {} }
1056 fn foo2() -> *const (impl Fn() + Sized) { loop {} }
1057 fn foo3() -> *const (impl Fn() + ?Sized) { loop {} }
1058 fn foo4() -> *const (impl Sized + Fn()) { loop {} }
1059 fn foo5() -> *const (impl ?Sized + Fn()) { loop {} }
1060 fn foo6() -> *const (impl Fn() + Trait) { loop {} }
1061 fn foo7() -> *const (impl Fn() + Sized + Trait) { loop {} }
1062 fn foo8() -> *const (impl Fn() + ?Sized + Trait) { loop {} }
1063 fn foo9() -> *const (impl Fn() -> u8 + ?Sized) { loop {} }
1064 fn foo10() -> *const (impl Fn() + Sized + ?Sized) { loop {} }
1068 // ^^^ *const impl Fn()
1070 // ^^^ *const impl Fn()
1072 // ^^^ *const (impl Fn() + ?Sized)
1074 // ^^^ *const impl Fn()
1076 // ^^^ *const (impl Fn() + ?Sized)
1078 // ^^^ *const (impl Fn() + Trait)
1080 // ^^^ *const (impl Fn() + Trait)
1082 // ^^^ *const (impl Fn() + Trait + ?Sized)
1084 // ^^^ *const (impl Fn() -> u8 + ?Sized)
1086 // ^^^ *const impl Fn()
1093 fn unit_structs_have_no_type_hints() {
1096 //- minicore: result
1097 struct SyntheticSyntax;
1102 Err(SyntheticSyntax) => (),
1109 fn let_statement() {
1112 #[derive(PartialEq)]
1113 enum Option<T> { None, Some(T) }
1115 #[derive(PartialEq)]
1116 struct Test { a: Option<u32>, b: u8 }
1119 struct InnerStruct {}
1129 let test = InnerStruct {};
1132 let test = unresolved();
1134 let test = (42, 'a');
1136 let (a, (b, (c,)) = (2, (3, (9.2,));
1148 //- minicore: option
1149 struct Test { a: Option<u32>, b: u8 }
1152 let test = Some(Test { a: Some(3), b: 1 });
1154 if let None = &test {};
1155 if let test = &test {};
1156 //^^^^ &Option<Test>
1157 if let Some(test) = &test {};
1159 if let Some(Test { a, b }) = &test {};
1160 //^ &Option<u32> ^ &u8
1161 if let Some(Test { a: x, b: y }) = &test {};
1162 //^ &Option<u32> ^ &u8
1163 if let Some(Test { a: Some(x), b: y }) = &test {};
1165 if let Some(Test { a: None, b: y }) = &test {};
1167 if let Some(Test { b: y, .. }) = &test {};
1178 //- minicore: option
1179 struct Test { a: Option<u32>, b: u8 }
1182 let test = Some(Test { a: Some(3), b: 1 });
1184 while let Some(Test { a: Some(x), b: y }) = &test {};
1191 fn match_arm_list() {
1194 //- minicore: option
1195 struct Test { a: Option<u32>, b: u8 }
1198 match Some(Test { a: Some(3), b: 1 }) {
1202 Some(Test { a: Some(x), b: y }) => (),
1211 fn incomplete_for_no_hint() {
1215 let data = &[1i32, 2, 3];
1222 pub struct Vec<T> {}
1225 pub fn new() -> Self { Vec {} }
1226 pub fn push(&mut self, t: T) {}
1229 impl<T> IntoIterator for Vec<T> {
1234 let mut data = Vec::new();
1239 println!("Unit expr");
1246 fn complete_for_hint() {
1249 //- minicore: iterator
1250 pub struct Vec<T> {}
1253 pub fn new() -> Self { Vec {} }
1254 pub fn push(&mut self, t: T) {}
1257 impl<T> IntoIterator for Vec<T> {
1262 let mut data = Vec::new();
1276 fn multi_dyn_trait_bounds() {
1279 pub struct Vec<T> {}
1282 pub fn new() -> Self { Vec {} }
1285 pub struct Box<T> {}
1291 // The block expression wrapping disables the constructor hint hiding logic
1292 let _v = { Vec::<Box<&(dyn Display + Sync)>>::new() };
1293 //^^ Vec<Box<&(dyn Display + Sync)>>
1294 let _v = { Vec::<Box<*const (dyn Display + Sync)>>::new() };
1295 //^^ Vec<Box<*const (dyn Display + Sync)>>
1296 let _v = { Vec::<Box<dyn Display + Sync>>::new() };
1297 //^^ Vec<Box<dyn Display + Sync>>
1304 fn shorten_iterator_hints() {
1307 //- minicore: iterators
1312 impl Iterator for MyIter {
1314 fn next(&mut self) -> Option<Self::Item> {
1322 let _x = iter::repeat(0);
1323 //^^ impl Iterator<Item = i32>
1324 fn generic<T: Clone>(t: T) {
1325 let _x = iter::repeat(t);
1326 //^^ impl Iterator<Item = T>
1327 let _chained = iter::repeat(t).take(10);
1328 //^^^^^^^^ impl Iterator<Item = T>
1336 fn skip_constructor_and_enum_type_hints() {
1340 parameter_hints: true,
1341 chaining_hints: true,
1342 hide_named_constructor_hints: true,
1346 //- minicore: try, option
1347 use core::ops::ControlFlow;
1350 pub mod y { pub struct Foo; }
1352 pub enum AnotherEnum {
1357 struct TupleStruct();
1363 fn try_new() -> ControlFlow<(), Self> {
1364 ControlFlow::Continue(Struct)
1368 struct Generic<T>(T);
1379 fn times2(value: i32) -> i32 {
1384 let enumb = Enum::Variant(0);
1386 let strukt = x::Foo;
1387 let strukt = x::y::Foo;
1388 let strukt = Struct;
1389 let strukt = Struct::new();
1391 let tuple_struct = TupleStruct();
1393 let generic0 = Generic::new();
1394 // ^^^^^^^^ Generic<i32>
1395 let generic1 = Generic(0);
1396 // ^^^^^^^^ Generic<i32>
1397 let generic2 = Generic::<i32>::new();
1398 let generic3 = <Generic<i32>>::new();
1399 let generic4 = Generic::<i32>(0);
1402 let option = Some(0);
1403 // ^^^^^^ Option<i32>
1405 // ^^^^ fn times2(i32) -> i32
1406 let closure = |x: i32| x * 2;
1407 // ^^^^^^^ |i32| -> i32
1410 fn fallible() -> ControlFlow<()> {
1411 let strukt = Struct::try_new()?;
1418 fn shows_constructor_type_hints_when_enabled() {
1422 use core::ops::ControlFlow;
1425 struct TupleStruct();
1431 fn try_new() -> ControlFlow<(), Self> {
1432 ControlFlow::Continue(Struct)
1436 struct Generic<T>(T);
1444 let strukt = Struct::new();
1446 let tuple_struct = TupleStruct();
1447 // ^^^^^^^^^^^^ TupleStruct
1448 let generic0 = Generic::new();
1449 // ^^^^^^^^ Generic<i32>
1450 let generic1 = Generic::<i32>::new();
1451 // ^^^^^^^^ Generic<i32>
1452 let generic2 = <Generic<i32>>::new();
1453 // ^^^^^^^^ Generic<i32>
1456 fn fallible() -> ControlFlow<()> {
1457 let strukt = Struct::try_new()?;
1471 (0..2).for_each(|increment| { start += increment; });
1475 //^^^^^^^^ |i32, i32| -> i32
1480 let _: i32 = multiply(1, 2);
1481 let multiply_ref = &multiply;
1482 //^^^^^^^^^^^^ &|i32, i32| -> i32
1484 let return_42 = || 42;
1485 //^^^^^^^^^ || -> i32
1491 fn hint_truncation() {
1493 InlayHintsConfig { max_length: Some(8), ..TEST_CONFIG },
1497 struct VeryLongOuterName<T>(T);
1502 let b = VeryLongOuterName(0usize);
1503 //^ VeryLongOuterName<…>
1504 let c = Smol(Smol(0u32))
1510 // Chaining hint tests
1513 fn chaining_hints_ignore_comments() {
1516 parameter_hints: false,
1518 chaining_hints: true,
1519 hide_named_constructor_hints: false,
1524 impl A { fn into_b(self) -> B { self.0 } }
1526 impl B { fn into_c(self) -> C { self.0 } }
1531 .into_b() // This is a comment
1532 // This is another comment
1554 fn chaining_hints_without_newlines() {
1558 impl A { fn into_b(self) -> B { self.0 } }
1560 impl B { fn into_c(self) -> C { self.0 } }
1564 let c = A(B(C)).into_b().into_c();
1570 fn struct_access_chaining_hints() {
1573 parameter_hints: false,
1575 chaining_hints: true,
1576 hide_named_constructor_hints: false,
1580 struct A { pub b: B }
1581 struct B { pub c: C }
1586 fn foo(&self) -> i32 { 42 }
1590 let x = A { b: B { c: C(true) } }
1615 fn generic_chaining_hints() {
1618 parameter_hints: false,
1620 chaining_hints: true,
1621 hide_named_constructor_hints: false,
1628 struct X<T,R>(T, R);
1631 fn new(t: T) -> Self { A(t) }
1632 fn into_b(self) -> B<T> { B(self.0) }
1635 fn into_c(self) -> C<T> { C(self.0) }
1638 let c = A::new(X(42, true))
1648 label: "B<X<i32, bool>>",
1653 label: "A<X<i32, bool>>",
1661 fn shorten_iterator_chaining_hints() {
1664 parameter_hints: false,
1666 chaining_hints: true,
1667 hide_named_constructor_hints: false,
1671 //- minicore: iterators
1676 impl Iterator for MyIter {
1678 fn next(&mut self) -> Option<Self::Item> {
1684 let _x = MyIter.by_ref()
1696 label: "impl Iterator<Item = ()>",
1701 label: "impl Iterator<Item = ()>",
1706 label: "impl Iterator<Item = ()>",
1711 label: "&mut MyIter",
1719 fn hints_in_attr_call() {
1723 //- proc_macros: identity, input_replace
1726 fn chain(self) -> Self {
1730 #[proc_macros::identity]
1732 let strukt = Struct;
1737 Struct::chain(strukt);
1759 kind: ParameterHint,