aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src/inlay_hints.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide/src/inlay_hints.rs')
-rw-r--r--crates/ra_ide/src/inlay_hints.rs1062
1 files changed, 316 insertions, 746 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs
index c87652555..4bbbcd258 100644
--- a/crates/ra_ide/src/inlay_hints.rs
+++ b/crates/ra_ide/src/inlay_hints.rs
@@ -1,14 +1,16 @@
1use hir::{Adt, HirDisplay, Semantics, Type}; 1use hir::{Adt, Callable, HirDisplay, Semantics, Type};
2use ra_ide_db::RootDatabase; 2use ra_ide_db::RootDatabase;
3use ra_prof::profile; 3use ra_prof::profile;
4use ra_syntax::{ 4use ra_syntax::{
5 ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, 5 ast::{self, ArgListOwner, AstNode},
6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, 6 match_ast, Direction, NodeOrToken, SmolStr, SyntaxKind, TextRange, T,
7}; 7};
8
9use crate::{FileId, FunctionSignature};
10use stdx::to_lower_snake_case; 8use stdx::to_lower_snake_case;
11 9
10use crate::FileId;
11use ast::NameOwner;
12use either::Either;
13
12#[derive(Clone, Debug, PartialEq, Eq)] 14#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct InlayHintsConfig { 15pub struct InlayHintsConfig {
14 pub type_hints: bool, 16 pub type_hints: bool,
@@ -94,7 +96,7 @@ fn get_chaining_hints(
94 return None; 96 return None;
95 } 97 }
96 98
97 if matches!(expr, ast::Expr::RecordLit(_)) { 99 if matches!(expr, ast::Expr::RecordExpr(_)) {
98 return None; 100 return None;
99 } 101 }
100 102
@@ -112,7 +114,7 @@ fn get_chaining_hints(
112 // Ignoring extra whitespace and comments 114 // Ignoring extra whitespace and comments
113 let next = tokens.next()?.kind(); 115 let next = tokens.next()?.kind();
114 let next_next = tokens.next()?.kind(); 116 let next_next = tokens.next()?.kind();
115 if next == SyntaxKind::WHITESPACE && next_next == SyntaxKind::DOT { 117 if next == SyntaxKind::WHITESPACE && next_next == T![.] {
116 let ty = sema.type_of_expr(&expr)?; 118 let ty = sema.type_of_expr(&expr)?;
117 if ty.is_unknown() { 119 if ty.is_unknown() {
118 return None; 120 return None;
@@ -150,19 +152,22 @@ fn get_param_name_hints(
150 _ => return None, 152 _ => return None,
151 }; 153 };
152 154
153 let fn_signature = get_fn_signature(sema, &expr)?; 155 let callable = get_callable(sema, &expr)?;
154 let n_params_to_skip = 156 let hints = callable
155 if fn_signature.has_self_param && matches!(&expr, ast::Expr::MethodCallExpr(_)) { 157 .params(sema.db)
156 1 158 .into_iter()
157 } else {
158 0
159 };
160 let hints = fn_signature
161 .parameter_names
162 .iter()
163 .skip(n_params_to_skip)
164 .zip(args) 159 .zip(args)
165 .filter(|(param, arg)| should_show_param_name_hint(sema, &fn_signature, param, &arg)) 160 .filter_map(|((param, _ty), arg)| match param? {
161 Either::Left(self_param) => Some((self_param.to_string(), arg)),
162 Either::Right(pat) => {
163 let param_name = match pat {
164 ast::Pat::BindPat(it) => it.name()?.to_string(),
165 it => it.to_string(),
166 };
167 Some((param_name, arg))
168 }
169 })
170 .filter(|(param_name, arg)| should_show_param_name_hint(sema, &callable, &param_name, &arg))
166 .map(|(param_name, arg)| InlayHint { 171 .map(|(param_name, arg)| InlayHint {
167 range: arg.syntax().text_range(), 172 range: arg.syntax().text_range(),
168 kind: InlayKind::ParameterHint, 173 kind: InlayKind::ParameterHint,
@@ -225,10 +230,10 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
225 match_ast! { 230 match_ast! {
226 match node { 231 match node {
227 ast::LetStmt(it) => { 232 ast::LetStmt(it) => {
228 return it.ascribed_type().is_some() 233 return it.ty().is_some()
229 }, 234 },
230 ast::Param(it) => { 235 ast::Param(it) => {
231 return it.ascribed_type().is_some() 236 return it.ty().is_some()
232 }, 237 },
233 ast::MatchArm(_it) => { 238 ast::MatchArm(_it) => {
234 return pat_is_enum_variant(db, bind_pat, pat_ty); 239 return pat_is_enum_variant(db, bind_pat, pat_ty);
@@ -250,28 +255,28 @@ fn should_not_display_type_hint(db: &RootDatabase, bind_pat: &ast::BindPat, pat_
250 255
251fn should_show_param_name_hint( 256fn should_show_param_name_hint(
252 sema: &Semantics<RootDatabase>, 257 sema: &Semantics<RootDatabase>,
253 fn_signature: &FunctionSignature, 258 callable: &Callable,
254 param_name: &str, 259 param_name: &str,
255 argument: &ast::Expr, 260 argument: &ast::Expr,
256) -> bool { 261) -> bool {
257 let param_name = param_name.trim_start_matches('_'); 262 let param_name = param_name.trim_start_matches('_');
263 let fn_name = match callable.kind() {
264 hir::CallableKind::Function(it) => Some(it.name(sema.db).to_string()),
265 hir::CallableKind::TupleStruct(_)
266 | hir::CallableKind::TupleEnumVariant(_)
267 | hir::CallableKind::Closure => None,
268 };
258 if param_name.is_empty() 269 if param_name.is_empty()
259 || Some(param_name) == fn_signature.name.as_ref().map(|s| s.trim_start_matches('_')) 270 || Some(param_name) == fn_name.as_ref().map(|s| s.trim_start_matches('_'))
260 || is_argument_similar_to_param_name(sema, argument, param_name) 271 || is_argument_similar_to_param_name(sema, argument, param_name)
261 || param_name.starts_with("ra_fixture") 272 || param_name.starts_with("ra_fixture")
262 { 273 {
263 return false; 274 return false;
264 } 275 }
265 276
266 let parameters_len = if fn_signature.has_self_param {
267 fn_signature.parameters.len() - 1
268 } else {
269 fn_signature.parameters.len()
270 };
271
272 // avoid displaying hints for common functions like map, filter, etc. 277 // avoid displaying hints for common functions like map, filter, etc.
273 // or other obvious words used in std 278 // or other obvious words used in std
274 !(parameters_len == 1 && is_obvious_param(param_name)) 279 !(callable.n_params() == 1 && is_obvious_param(param_name))
275} 280}
276 281
277fn is_argument_similar_to_param_name( 282fn is_argument_similar_to_param_name(
@@ -318,610 +323,264 @@ fn is_obvious_param(param_name: &str) -> bool {
318 param_name.len() == 1 || is_obvious_param_name 323 param_name.len() == 1 || is_obvious_param_name
319} 324}
320 325
321fn get_fn_signature(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<FunctionSignature> { 326fn get_callable(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<Callable> {
322 match expr { 327 match expr {
323 ast::Expr::CallExpr(expr) => { 328 ast::Expr::CallExpr(expr) => sema.type_of_expr(&expr.expr()?)?.as_callable(sema.db),
324 // FIXME: Type::as_callable is broken for closures 329 ast::Expr::MethodCallExpr(expr) => sema.resolve_method_call_as_callable(expr),
325 let callable_def = sema.type_of_expr(&expr.expr()?)?.as_callable()?;
326 match callable_def {
327 hir::CallableDef::FunctionId(it) => {
328 Some(FunctionSignature::from_hir(sema.db, it.into()))
329 }
330 hir::CallableDef::StructId(it) => {
331 FunctionSignature::from_struct(sema.db, it.into())
332 }
333 hir::CallableDef::EnumVariantId(it) => {
334 FunctionSignature::from_enum_variant(sema.db, it.into())
335 }
336 }
337 }
338 ast::Expr::MethodCallExpr(expr) => {
339 let fn_def = sema.resolve_method_call(&expr)?;
340 Some(FunctionSignature::from_hir(sema.db, fn_def))
341 }
342 _ => None, 330 _ => None,
343 } 331 }
344} 332}
345 333
346#[cfg(test)] 334#[cfg(test)]
347mod tests { 335mod tests {
348 use crate::inlay_hints::InlayHintsConfig; 336 use expect::{expect, Expect};
349 use insta::assert_debug_snapshot; 337 use test_utils::extract_annotations;
338
339 use crate::{inlay_hints::InlayHintsConfig, mock_analysis::single_file};
340
341 fn check(ra_fixture: &str) {
342 check_with_config(InlayHintsConfig::default(), ra_fixture);
343 }
344
345 fn check_with_config(config: InlayHintsConfig, ra_fixture: &str) {
346 let (analysis, file_id) = single_file(ra_fixture);
347 let expected = extract_annotations(&*analysis.file_text(file_id).unwrap());
348 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
349 let actual =
350 inlay_hints.into_iter().map(|it| (it.range, it.label.to_string())).collect::<Vec<_>>();
351 assert_eq!(expected, actual, "\nExpected:\n{:#?}\n\nActual:\n{:#?}", expected, actual);
352 }
350 353
351 use crate::mock_analysis::single_file; 354 fn check_expect(config: InlayHintsConfig, ra_fixture: &str, expect: Expect) {
355 let (analysis, file_id) = single_file(ra_fixture);
356 let inlay_hints = analysis.inlay_hints(file_id, &config).unwrap();
357 expect.assert_debug_eq(&inlay_hints)
358 }
352 359
353 #[test] 360 #[test]
354 fn param_hints_only() { 361 fn param_hints_only() {
355 let (analysis, file_id) = single_file( 362 check_with_config(
363 InlayHintsConfig {
364 parameter_hints: true,
365 type_hints: false,
366 chaining_hints: false,
367 max_length: None,
368 },
356 r#" 369 r#"
357 fn foo(a: i32, b: i32) -> i32 { a + b } 370fn foo(a: i32, b: i32) -> i32 { a + b }
358 fn main() { 371fn main() {
359 let _x = foo(4, 4); 372 let _x = foo(
360 }"#, 373 4,
374 //^ a
375 4,
376 //^ b
377 );
378}"#,
361 ); 379 );
362 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: true, type_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
363 [
364 InlayHint {
365 range: 69..70,
366 kind: ParameterHint,
367 label: "a",
368 },
369 InlayHint {
370 range: 72..73,
371 kind: ParameterHint,
372 label: "b",
373 },
374 ]
375 "###);
376 } 380 }
377 381
378 #[test] 382 #[test]
379 fn hints_disabled() { 383 fn hints_disabled() {
380 let (analysis, file_id) = single_file( 384 check_with_config(
385 InlayHintsConfig {
386 type_hints: false,
387 parameter_hints: false,
388 chaining_hints: false,
389 max_length: None,
390 },
381 r#" 391 r#"
382 fn foo(a: i32, b: i32) -> i32 { a + b } 392fn foo(a: i32, b: i32) -> i32 { a + b }
383 fn main() { 393fn main() {
384 let _x = foo(4, 4); 394 let _x = foo(4, 4);
385 }"#, 395}"#,
386 ); 396 );
387 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: false, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"[]"###);
388 } 397 }
389 398
390 #[test] 399 #[test]
391 fn type_hints_only() { 400 fn type_hints_only() {
392 let (analysis, file_id) = single_file( 401 check_with_config(
402 InlayHintsConfig {
403 type_hints: true,
404 parameter_hints: false,
405 chaining_hints: false,
406 max_length: None,
407 },
393 r#" 408 r#"
394 fn foo(a: i32, b: i32) -> i32 { a + b } 409fn foo(a: i32, b: i32) -> i32 { a + b }
395 fn main() { 410fn main() {
396 let _x = foo(4, 4); 411 let _x = foo(4, 4);
397 }"#, 412 //^^ i32
413}"#,
398 ); 414 );
399 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ type_hints: true, parameter_hints: false, chaining_hints: false, max_length: None}).unwrap(), @r###"
400 [
401 InlayHint {
402 range: 60..62,
403 kind: TypeHint,
404 label: "i32",
405 },
406 ]
407 "###);
408 } 415 }
416
409 #[test] 417 #[test]
410 fn default_generic_types_should_not_be_displayed() { 418 fn default_generic_types_should_not_be_displayed() {
411 let (analysis, file_id) = single_file( 419 check(
412 r#" 420 r#"
413struct Test<K, T = u8> { 421struct Test<K, T = u8> { k: K, t: T }
414 k: K,
415 t: T,
416}
417 422
418fn main() { 423fn main() {
419 let zz = Test { t: 23u8, k: 33 }; 424 let zz = Test { t: 23u8, k: 33 };
425 //^^ Test<i32>
420 let zz_ref = &zz; 426 let zz_ref = &zz;
427 //^^^^^^ &Test<i32>
428 let test = || zz;
429 //^^^^ || -> Test<i32>
421}"#, 430}"#,
422 ); 431 );
423
424 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
425 [
426 InlayHint {
427 range: 68..70,
428 kind: TypeHint,
429 label: "Test<i32>",
430 },
431 InlayHint {
432 range: 106..112,
433 kind: TypeHint,
434 label: "&Test<i32>",
435 },
436 ]
437 "###
438 );
439 } 432 }
440 433
441 #[test] 434 #[test]
442 fn let_statement() { 435 fn let_statement() {
443 let (analysis, file_id) = single_file( 436 check(
444 r#" 437 r#"
445#[derive(PartialEq)] 438#[derive(PartialEq)]
446enum CustomOption<T> { 439enum Option<T> { None, Some(T) }
447 None,
448 Some(T),
449}
450 440
451#[derive(PartialEq)] 441#[derive(PartialEq)]
452struct Test { 442struct Test { a: Option<u32>, b: u8 }
453 a: CustomOption<u32>,
454 b: u8,
455}
456 443
457fn main() { 444fn main() {
458 struct InnerStruct {} 445 struct InnerStruct {}
459 446
460 let test = 54; 447 let test = 54;
448 //^^^^ i32
461 let test: i32 = 33; 449 let test: i32 = 33;
462 let mut test = 33; 450 let mut test = 33;
451 //^^^^^^^^ i32
463 let _ = 22; 452 let _ = 22;
464 let test = "test"; 453 let test = "test";
454 //^^^^ &str
465 let test = InnerStruct {}; 455 let test = InnerStruct {};
466 456
467 let test = vec![222]; 457 let test = unresolved();
468 let test: Vec<_> = (0..3).collect();
469 let test = (0..3).collect::<Vec<i128>>();
470 let test = (0..3).collect::<Vec<_>>();
471
472 let mut test = Vec::new();
473 test.push(333);
474 458
475 let test = (42, 'a'); 459 let test = (42, 'a');
476 let (a, (b, c, (d, e), f)) = (2, (3, 4, (6.6, 7.7), 5)); 460 //^^^^ (i32, char)
461 let (a, (b, (c,)) = (2, (3, (9.2,));
462 //^ i32 ^ i32 ^ f64
477 let &x = &92; 463 let &x = &92;
464 //^ i32
478}"#, 465}"#,
479 ); 466 );
480
481 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
482 [
483 InlayHint {
484 range: 192..196,
485 kind: TypeHint,
486 label: "i32",
487 },
488 InlayHint {
489 range: 235..243,
490 kind: TypeHint,
491 label: "i32",
492 },
493 InlayHint {
494 range: 274..278,
495 kind: TypeHint,
496 label: "&str",
497 },
498 InlayHint {
499 range: 538..542,
500 kind: TypeHint,
501 label: "(i32, char)",
502 },
503 InlayHint {
504 range: 565..566,
505 kind: TypeHint,
506 label: "i32",
507 },
508 InlayHint {
509 range: 569..570,
510 kind: TypeHint,
511 label: "i32",
512 },
513 InlayHint {
514 range: 572..573,
515 kind: TypeHint,
516 label: "i32",
517 },
518 InlayHint {
519 range: 576..577,
520 kind: TypeHint,
521 label: "f64",
522 },
523 InlayHint {
524 range: 579..580,
525 kind: TypeHint,
526 label: "f64",
527 },
528 InlayHint {
529 range: 583..584,
530 kind: TypeHint,
531 label: "i32",
532 },
533 InlayHint {
534 range: 626..627,
535 kind: TypeHint,
536 label: "i32",
537 },
538 ]
539 "###
540 );
541 } 467 }
542 468
543 #[test] 469 #[test]
544 fn closure_parameters() { 470 fn closure_parameters() {
545 let (analysis, file_id) = single_file( 471 check(
546 r#" 472 r#"
547fn main() { 473fn main() {
548 let mut start = 0; 474 let mut start = 0;
549 (0..2).for_each(|increment| { 475 //^^^^^^^^^ i32
550 start += increment; 476 (0..2).for_each(|increment| { start += increment; });
551 }); 477 //^^^^^^^^^ i32
478
479 let multiply =
480 //^^^^^^^^ |…| -> i32
481 | a, b| a * b
482 //^ i32 ^ i32
483 ;
552 484
553 let multiply = |a, b, c, d| a * b * c * d; 485 let _: i32 = multiply(1, 2);
554 let _: i32 = multiply(1, 2, 3, 4);
555 let multiply_ref = &multiply; 486 let multiply_ref = &multiply;
487 //^^^^^^^^^^^^ &|…| -> i32
556 488
557 let return_42 = || 42; 489 let return_42 = || 42;
490 //^^^^^^^^^ || -> i32
558}"#, 491}"#,
559 ); 492 );
560
561 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
562 [
563 InlayHint {
564 range: 20..29,
565 kind: TypeHint,
566 label: "i32",
567 },
568 InlayHint {
569 range: 56..65,
570 kind: TypeHint,
571 label: "i32",
572 },
573 InlayHint {
574 range: 114..122,
575 kind: TypeHint,
576 label: "|…| -> i32",
577 },
578 InlayHint {
579 range: 126..127,
580 kind: TypeHint,
581 label: "i32",
582 },
583 InlayHint {
584 range: 129..130,
585 kind: TypeHint,
586 label: "i32",
587 },
588 InlayHint {
589 range: 132..133,
590 kind: TypeHint,
591 label: "i32",
592 },
593 InlayHint {
594 range: 135..136,
595 kind: TypeHint,
596 label: "i32",
597 },
598 InlayHint {
599 range: 200..212,
600 kind: TypeHint,
601 label: "&|…| -> i32",
602 },
603 InlayHint {
604 range: 235..244,
605 kind: TypeHint,
606 label: "|| -> i32",
607 },
608 ]
609 "###
610 );
611 } 493 }
612 494
613 #[test] 495 #[test]
614 fn for_expression() { 496 fn for_expression() {
615 let (analysis, file_id) = single_file( 497 check(
616 r#" 498 r#"
617fn main() { 499fn main() {
618 let mut start = 0; 500 let mut start = 0;
619 for increment in 0..2 { 501 //^^^^^^^^^ i32
620 start += increment; 502 for increment in 0..2 { start += increment; }
621 } 503 //^^^^^^^^^ i32
622}"#, 504}"#,
623 ); 505 );
624
625 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
626 [
627 InlayHint {
628 range: 20..29,
629 kind: TypeHint,
630 label: "i32",
631 },
632 InlayHint {
633 range: 43..52,
634 kind: TypeHint,
635 label: "i32",
636 },
637 ]
638 "###
639 );
640 } 506 }
641 507
642 #[test] 508 #[test]
643 fn if_expr() { 509 fn if_expr() {
644 let (analysis, file_id) = single_file( 510 check(
645 r#" 511 r#"
646#[derive(PartialEq)] 512enum Option<T> { None, Some(T) }
647enum CustomOption<T> { 513use Option::*;
648 None,
649 Some(T),
650}
651
652#[derive(PartialEq)]
653struct Test {
654 a: CustomOption<u32>,
655 b: u8,
656}
657 514
658use CustomOption::*; 515struct Test { a: Option<u32>, b: u8 }
659 516
660fn main() { 517fn main() {
661 let test = Some(Test { a: Some(3), b: 1 }); 518 let test = Some(Test { a: Some(3), b: 1 });
519 //^^^^ Option<Test>
662 if let None = &test {}; 520 if let None = &test {};
663 if let test = &test {}; 521 if let test = &test {};
522 //^^^^ &Option<Test>
664 if let Some(test) = &test {}; 523 if let Some(test) = &test {};
665 if let Some(Test { a, b }) = &test {}; 524 //^^^^ &Test
666 if let Some(Test { a: x, b: y }) = &test {}; 525 if let Some(Test { a, b }) = &test {};
667 if let Some(Test { a: Some(x), b: y }) = &test {}; 526 //^ &Option<u32> ^ &u8
668 if let Some(Test { a: None, b: y }) = &test {}; 527 if let Some(Test { a: x, b: y }) = &test {};
528 //^ &Option<u32> ^ &u8
529 if let Some(Test { a: Some(x), b: y }) = &test {};
530 //^ &u32 ^ &u8
531 if let Some(Test { a: None, b: y }) = &test {};
532 //^ &u8
669 if let Some(Test { b: y, .. }) = &test {}; 533 if let Some(Test { b: y, .. }) = &test {};
670 534 //^ &u8
671 if test == None {} 535 if test == None {}
672}"#, 536}"#,
673 ); 537 );
674
675 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
676 [
677 InlayHint {
678 range: 187..191,
679 kind: TypeHint,
680 label: "CustomOption<Test>",
681 },
682 InlayHint {
683 range: 266..270,
684 kind: TypeHint,
685 label: "&CustomOption<Test>",
686 },
687 InlayHint {
688 range: 299..303,
689 kind: TypeHint,
690 label: "&Test",
691 },
692 InlayHint {
693 range: 340..341,
694 kind: TypeHint,
695 label: "&CustomOption<u32>",
696 },
697 InlayHint {
698 range: 343..344,
699 kind: TypeHint,
700 label: "&u8",
701 },
702 InlayHint {
703 range: 386..387,
704 kind: TypeHint,
705 label: "&CustomOption<u32>",
706 },
707 InlayHint {
708 range: 392..393,
709 kind: TypeHint,
710 label: "&u8",
711 },
712 InlayHint {
713 range: 440..441,
714 kind: TypeHint,
715 label: "&u32",
716 },
717 InlayHint {
718 range: 447..448,
719 kind: TypeHint,
720 label: "&u8",
721 },
722 InlayHint {
723 range: 499..500,
724 kind: TypeHint,
725 label: "&u8",
726 },
727 InlayHint {
728 range: 542..543,
729 kind: TypeHint,
730 label: "&u8",
731 },
732 ]
733 "###
734 );
735 } 538 }
736 539
737 #[test] 540 #[test]
738 fn while_expr() { 541 fn while_expr() {
739 let (analysis, file_id) = single_file( 542 check(
740 r#" 543 r#"
741#[derive(PartialEq)] 544enum Option<T> { None, Some(T) }
742enum CustomOption<T> { 545use Option::*;
743 None,
744 Some(T),
745}
746 546
747#[derive(PartialEq)] 547struct Test { a: Option<u32>, b: u8 }
748struct Test {
749 a: CustomOption<u32>,
750 b: u8,
751}
752
753use CustomOption::*;
754 548
755fn main() { 549fn main() {
756 let test = Some(Test { a: Some(3), b: 1 }); 550 let test = Some(Test { a: Some(3), b: 1 });
757 while let None = &test {}; 551 //^^^^ Option<Test>
758 while let test = &test {}; 552 while let Some(Test { a: Some(x), b: y }) = &test {};
759 while let Some(test) = &test {}; 553 //^ &u32 ^ &u8
760 while let Some(Test { a, b }) = &test {};
761 while let Some(Test { a: x, b: y }) = &test {};
762 while let Some(Test { a: Some(x), b: y }) = &test {};
763 while let Some(Test { a: None, b: y }) = &test {};
764 while let Some(Test { b: y, .. }) = &test {};
765
766 while test == None {}
767}"#, 554}"#,
768 ); 555 );
769
770 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
771 [
772 InlayHint {
773 range: 187..191,
774 kind: TypeHint,
775 label: "CustomOption<Test>",
776 },
777 InlayHint {
778 range: 272..276,
779 kind: TypeHint,
780 label: "&CustomOption<Test>",
781 },
782 InlayHint {
783 range: 308..312,
784 kind: TypeHint,
785 label: "&Test",
786 },
787 InlayHint {
788 range: 352..353,
789 kind: TypeHint,
790 label: "&CustomOption<u32>",
791 },
792 InlayHint {
793 range: 355..356,
794 kind: TypeHint,
795 label: "&u8",
796 },
797 InlayHint {
798 range: 401..402,
799 kind: TypeHint,
800 label: "&CustomOption<u32>",
801 },
802 InlayHint {
803 range: 407..408,
804 kind: TypeHint,
805 label: "&u8",
806 },
807 InlayHint {
808 range: 458..459,
809 kind: TypeHint,
810 label: "&u32",
811 },
812 InlayHint {
813 range: 465..466,
814 kind: TypeHint,
815 label: "&u8",
816 },
817 InlayHint {
818 range: 520..521,
819 kind: TypeHint,
820 label: "&u8",
821 },
822 InlayHint {
823 range: 566..567,
824 kind: TypeHint,
825 label: "&u8",
826 },
827 ]
828 "###
829 );
830 } 556 }
831 557
832 #[test] 558 #[test]
833 fn match_arm_list() { 559 fn match_arm_list() {
834 let (analysis, file_id) = single_file( 560 check(
835 r#" 561 r#"
836#[derive(PartialEq)] 562enum Option<T> { None, Some(T) }
837enum CustomOption<T> { 563use Option::*;
838 None,
839 Some(T),
840}
841 564
842#[derive(PartialEq)] 565struct Test { a: Option<u32>, b: u8 }
843struct Test {
844 a: CustomOption<u32>,
845 b: u8,
846}
847
848use CustomOption::*;
849 566
850fn main() { 567fn main() {
851 match Some(Test { a: Some(3), b: 1 }) { 568 match Some(Test { a: Some(3), b: 1 }) {
852 None => (), 569 None => (),
853 test => (), 570 test => (),
854 Some(test) => (), 571 //^^^^ Option<Test>
855 Some(Test { a, b }) => (),
856 Some(Test { a: x, b: y }) => (),
857 Some(Test { a: Some(x), b: y }) => (), 572 Some(Test { a: Some(x), b: y }) => (),
858 Some(Test { a: None, b: y }) => (), 573 //^ u32 ^ u8
859 Some(Test { b: y, .. }) => (),
860 _ => {} 574 _ => {}
861 } 575 }
862}"#, 576}"#,
863 ); 577 );
864
865 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
866 [
867 InlayHint {
868 range: 251..255,
869 kind: TypeHint,
870 label: "CustomOption<Test>",
871 },
872 InlayHint {
873 range: 276..280,
874 kind: TypeHint,
875 label: "Test",
876 },
877 InlayHint {
878 range: 309..310,
879 kind: TypeHint,
880 label: "CustomOption<u32>",
881 },
882 InlayHint {
883 range: 312..313,
884 kind: TypeHint,
885 label: "u8",
886 },
887 InlayHint {
888 range: 347..348,
889 kind: TypeHint,
890 label: "CustomOption<u32>",
891 },
892 InlayHint {
893 range: 353..354,
894 kind: TypeHint,
895 label: "u8",
896 },
897 InlayHint {
898 range: 393..394,
899 kind: TypeHint,
900 label: "u32",
901 },
902 InlayHint {
903 range: 400..401,
904 kind: TypeHint,
905 label: "u8",
906 },
907 InlayHint {
908 range: 444..445,
909 kind: TypeHint,
910 label: "u8",
911 },
912 InlayHint {
913 range: 479..480,
914 kind: TypeHint,
915 label: "u8",
916 },
917 ]
918 "###
919 );
920 } 578 }
921 579
922 #[test] 580 #[test]
923 fn hint_truncation() { 581 fn hint_truncation() {
924 let (analysis, file_id) = single_file( 582 check_with_config(
583 InlayHintsConfig { max_length: Some(8), ..Default::default() },
925 r#" 584 r#"
926struct Smol<T>(T); 585struct Smol<T>(T);
927 586
@@ -929,52 +588,25 @@ struct VeryLongOuterName<T>(T);
929 588
930fn main() { 589fn main() {
931 let a = Smol(0u32); 590 let a = Smol(0u32);
591 //^ Smol<u32>
932 let b = VeryLongOuterName(0usize); 592 let b = VeryLongOuterName(0usize);
593 //^ VeryLongOuterName<…>
933 let c = Smol(Smol(0u32)) 594 let c = Smol(Smol(0u32))
595 //^ Smol<Smol<…>>
934}"#, 596}"#,
935 ); 597 );
936
937 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
938 [
939 InlayHint {
940 range: 73..74,
941 kind: TypeHint,
942 label: "Smol<u32>",
943 },
944 InlayHint {
945 range: 97..98,
946 kind: TypeHint,
947 label: "VeryLongOuterName<…>",
948 },
949 InlayHint {
950 range: 136..137,
951 kind: TypeHint,
952 label: "Smol<Smol<…>>",
953 },
954 ]
955 "###
956 );
957 } 598 }
958 599
959 #[test] 600 #[test]
960 fn function_call_parameter_hint() { 601 fn function_call_parameter_hint() {
961 let (analysis, file_id) = single_file( 602 check(
962 r#" 603 r#"
963enum CustomOption<T> { 604enum Option<T> { None, Some(T) }
964 None, 605use Option::*;
965 Some(T),
966}
967use CustomOption::*;
968 606
969struct FileId {} 607struct FileId {}
970struct SmolStr {} 608struct SmolStr {}
971 609
972impl From<&str> for SmolStr {
973 fn from(_: &str) -> Self {
974 unimplemented!()
975 }
976}
977
978struct TextRange {} 610struct TextRange {}
979struct SyntaxKind {} 611struct SyntaxKind {}
980struct NavigationTarget {} 612struct NavigationTarget {}
@@ -982,18 +614,15 @@ struct NavigationTarget {}
982struct Test {} 614struct Test {}
983 615
984impl Test { 616impl Test {
985 fn method(&self, mut param: i32) -> i32 { 617 fn method(&self, mut param: i32) -> i32 { param * 2 }
986 param * 2
987 }
988 618
989 fn from_syntax( 619 fn from_syntax(
990 file_id: FileId, 620 file_id: FileId,
991 name: SmolStr, 621 name: SmolStr,
992 focus_range: CustomOption<TextRange>, 622 focus_range: Option<TextRange>,
993 full_range: TextRange, 623 full_range: TextRange,
994 kind: SyntaxKind, 624 kind: SyntaxKind,
995 docs: CustomOption<String>, 625 docs: Option<String>,
996 description: CustomOption<String>,
997 ) -> NavigationTarget { 626 ) -> NavigationTarget {
998 NavigationTarget {} 627 NavigationTarget {}
999 } 628 }
@@ -1005,108 +634,36 @@ fn test_func(mut foo: i32, bar: i32, msg: &str, _: i32, last: i32) -> i32 {
1005 634
1006fn main() { 635fn main() {
1007 let not_literal = 1; 636 let not_literal = 1;
1008 let _: i32 = test_func(1, 2, "hello", 3, not_literal); 637 //^^^^^^^^^^^ i32
638 let _: i32 = test_func(1, 2, "hello", 3, not_literal);
639 //^ foo ^ bar ^^^^^^^ msg ^^^^^^^^^^^ last
1009 let t: Test = Test {}; 640 let t: Test = Test {};
1010 t.method(123); 641 t.method(123);
1011 Test::method(&t, 3456); 642 //^^^ param
1012 643 Test::method(&t, 3456);
644 //^^ &self ^^^^ param
1013 Test::from_syntax( 645 Test::from_syntax(
1014 FileId {}, 646 FileId {},
647 //^^^^^^^^^ file_id
1015 "impl".into(), 648 "impl".into(),
649 //^^^^^^^^^^^^^ name
1016 None, 650 None,
651 //^^^^ focus_range
1017 TextRange {}, 652 TextRange {},
653 //^^^^^^^^^^^^ full_range
1018 SyntaxKind {}, 654 SyntaxKind {},
655 //^^^^^^^^^^^^^ kind
1019 None, 656 None,
1020 None, 657 //^^^^ docs
1021 ); 658 );
1022}"#, 659}"#,
1023 ); 660 );
1024
1025 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig::default()).unwrap(), @r###"
1026 [
1027 InlayHint {
1028 range: 797..808,
1029 kind: TypeHint,
1030 label: "i32",
1031 },
1032 InlayHint {
1033 range: 841..842,
1034 kind: ParameterHint,
1035 label: "foo",
1036 },
1037 InlayHint {
1038 range: 844..845,
1039 kind: ParameterHint,
1040 label: "bar",
1041 },
1042 InlayHint {
1043 range: 847..854,
1044 kind: ParameterHint,
1045 label: "msg",
1046 },
1047 InlayHint {
1048 range: 859..870,
1049 kind: ParameterHint,
1050 label: "last",
1051 },
1052 InlayHint {
1053 range: 913..916,
1054 kind: ParameterHint,
1055 label: "param",
1056 },
1057 InlayHint {
1058 range: 936..938,
1059 kind: ParameterHint,
1060 label: "&self",
1061 },
1062 InlayHint {
1063 range: 940..944,
1064 kind: ParameterHint,
1065 label: "param",
1066 },
1067 InlayHint {
1068 range: 979..988,
1069 kind: ParameterHint,
1070 label: "file_id",
1071 },
1072 InlayHint {
1073 range: 998..1011,
1074 kind: ParameterHint,
1075 label: "name",
1076 },
1077 InlayHint {
1078 range: 1021..1025,
1079 kind: ParameterHint,
1080 label: "focus_range",
1081 },
1082 InlayHint {
1083 range: 1035..1047,
1084 kind: ParameterHint,
1085 label: "full_range",
1086 },
1087 InlayHint {
1088 range: 1057..1070,
1089 kind: ParameterHint,
1090 label: "kind",
1091 },
1092 InlayHint {
1093 range: 1080..1084,
1094 kind: ParameterHint,
1095 label: "docs",
1096 },
1097 InlayHint {
1098 range: 1094..1098,
1099 kind: ParameterHint,
1100 label: "description",
1101 },
1102 ]
1103 "###
1104 );
1105 } 661 }
1106 662
1107 #[test] 663 #[test]
1108 fn omitted_parameters_hints_heuristics() { 664 fn omitted_parameters_hints_heuristics() {
1109 let (analysis, file_id) = single_file( 665 check_with_config(
666 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1110 r#" 667 r#"
1111fn map(f: i32) {} 668fn map(f: i32) {}
1112fn filter(predicate: i32) {} 669fn filter(predicate: i32) {}
@@ -1188,22 +745,15 @@ fn main() {
1188 let _: f64 = a.abs_sub(b); 745 let _: f64 = a.abs_sub(b);
1189}"#, 746}"#,
1190 ); 747 );
1191
1192 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1193 []
1194 "###
1195 );
1196 } 748 }
1197 749
1198 #[test] 750 #[test]
1199 fn unit_structs_have_no_type_hints() { 751 fn unit_structs_have_no_type_hints() {
1200 let (analysis, file_id) = single_file( 752 check_with_config(
753 InlayHintsConfig { max_length: Some(8), ..Default::default() },
1201 r#" 754 r#"
1202enum CustomResult<T, E> { 755enum Result<T, E> { Ok(T), Err(E) }
1203 Ok(T), 756use Result::*;
1204 Err(E),
1205}
1206use CustomResult::*;
1207 757
1208struct SyntheticSyntax; 758struct SyntheticSyntax;
1209 759
@@ -1214,135 +764,155 @@ fn main() {
1214 } 764 }
1215}"#, 765}"#,
1216 ); 766 );
1217
1218 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig { max_length: Some(8), ..Default::default() }).unwrap(), @r###"
1219 []
1220 "###
1221 );
1222 } 767 }
1223 768
1224 #[test] 769 #[test]
1225 fn chaining_hints_ignore_comments() { 770 fn chaining_hints_ignore_comments() {
1226 let (analysis, file_id) = single_file( 771 check_expect(
772 InlayHintsConfig {
773 parameter_hints: false,
774 type_hints: false,
775 chaining_hints: true,
776 max_length: None,
777 },
1227 r#" 778 r#"
1228 struct A(B); 779struct A(B);
1229 impl A { fn into_b(self) -> B { self.0 } } 780impl A { fn into_b(self) -> B { self.0 } }
1230 struct B(C); 781struct B(C);
1231 impl B { fn into_c(self) -> C { self.0 } } 782impl B { fn into_c(self) -> C { self.0 } }
1232 struct C; 783struct C;
1233 784
1234 fn main() { 785fn main() {
1235 let c = A(B(C)) 786 let c = A(B(C))
1236 .into_b() // This is a comment 787 .into_b() // This is a comment
1237 .into_c(); 788 .into_c();
1238 }"#, 789}
790"#,
791 expect![[r#"
792 [
793 InlayHint {
794 range: 147..172,
795 kind: ChainingHint,
796 label: "B",
797 },
798 InlayHint {
799 range: 147..154,
800 kind: ChainingHint,
801 label: "A",
802 },
803 ]
804 "#]],
1239 ); 805 );
1240 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1241 [
1242 InlayHint {
1243 range: 147..172,
1244 kind: ChainingHint,
1245 label: "B",
1246 },
1247 InlayHint {
1248 range: 147..154,
1249 kind: ChainingHint,
1250 label: "A",
1251 },
1252 ]
1253 "###);
1254 } 806 }
1255 807
1256 #[test] 808 #[test]
1257 fn chaining_hints_without_newlines() { 809 fn chaining_hints_without_newlines() {
1258 let (analysis, file_id) = single_file( 810 check_with_config(
811 InlayHintsConfig {
812 parameter_hints: false,
813 type_hints: false,
814 chaining_hints: true,
815 max_length: None,
816 },
1259 r#" 817 r#"
1260 struct A(B); 818struct A(B);
1261 impl A { fn into_b(self) -> B { self.0 } } 819impl A { fn into_b(self) -> B { self.0 } }
1262 struct B(C); 820struct B(C);
1263 impl B { fn into_c(self) -> C { self.0 } } 821impl B { fn into_c(self) -> C { self.0 } }
1264 struct C; 822struct C;
1265 823
1266 fn main() { 824fn main() {
1267 let c = A(B(C)).into_b().into_c(); 825 let c = A(B(C)).into_b().into_c();
1268 }"#, 826}"#,
1269 ); 827 );
1270 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"[]"###);
1271 } 828 }
1272 829
1273 #[test] 830 #[test]
1274 fn struct_access_chaining_hints() { 831 fn struct_access_chaining_hints() {
1275 let (analysis, file_id) = single_file( 832 check_expect(
833 InlayHintsConfig {
834 parameter_hints: false,
835 type_hints: false,
836 chaining_hints: true,
837 max_length: None,
838 },
1276 r#" 839 r#"
1277 struct A { pub b: B } 840struct A { pub b: B }
1278 struct B { pub c: C } 841struct B { pub c: C }
1279 struct C(pub bool); 842struct C(pub bool);
1280 struct D; 843struct D;
1281 844
1282 impl D { 845impl D {
1283 fn foo(&self) -> i32 { 42 } 846 fn foo(&self) -> i32 { 42 }
1284 } 847}
1285 848
1286 fn main() { 849fn main() {
1287 let x = A { b: B { c: C(true) } } 850 let x = A { b: B { c: C(true) } }
1288 .b 851 .b
1289 .c 852 .c
1290 .0; 853 .0;
1291 let x = D 854 let x = D
1292 .foo(); 855 .foo();
1293 }"#, 856}"#,
857 expect![[r#"
858 [
859 InlayHint {
860 range: 143..190,
861 kind: ChainingHint,
862 label: "C",
863 },
864 InlayHint {
865 range: 143..179,
866 kind: ChainingHint,
867 label: "B",
868 },
869 ]
870 "#]],
1294 ); 871 );
1295 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1296 [
1297 InlayHint {
1298 range: 143..190,
1299 kind: ChainingHint,
1300 label: "C",
1301 },
1302 InlayHint {
1303 range: 143..179,
1304 kind: ChainingHint,
1305 label: "B",
1306 },
1307 ]
1308 "###);
1309 } 872 }
1310 873
1311 #[test] 874 #[test]
1312 fn generic_chaining_hints() { 875 fn generic_chaining_hints() {
1313 let (analysis, file_id) = single_file( 876 check_expect(
877 InlayHintsConfig {
878 parameter_hints: false,
879 type_hints: false,
880 chaining_hints: true,
881 max_length: None,
882 },
1314 r#" 883 r#"
1315 struct A<T>(T); 884struct A<T>(T);
1316 struct B<T>(T); 885struct B<T>(T);
1317 struct C<T>(T); 886struct C<T>(T);
1318 struct X<T,R>(T, R); 887struct X<T,R>(T, R);
1319 888
1320 impl<T> A<T> { 889impl<T> A<T> {
1321 fn new(t: T) -> Self { A(t) } 890 fn new(t: T) -> Self { A(t) }
1322 fn into_b(self) -> B<T> { B(self.0) } 891 fn into_b(self) -> B<T> { B(self.0) }
1323 } 892}
1324 impl<T> B<T> { 893impl<T> B<T> {
1325 fn into_c(self) -> C<T> { C(self.0) } 894 fn into_c(self) -> C<T> { C(self.0) }
1326 } 895}
1327 fn main() { 896fn main() {
1328 let c = A::new(X(42, true)) 897 let c = A::new(X(42, true))
1329 .into_b() 898 .into_b()
1330 .into_c(); 899 .into_c();
1331 }"#, 900}
901"#,
902 expect![[r#"
903 [
904 InlayHint {
905 range: 246..283,
906 kind: ChainingHint,
907 label: "B<X<i32, bool>>",
908 },
909 InlayHint {
910 range: 246..265,
911 kind: ChainingHint,
912 label: "A<X<i32, bool>>",
913 },
914 ]
915 "#]],
1332 ); 916 );
1333 assert_debug_snapshot!(analysis.inlay_hints(file_id, &InlayHintsConfig{ parameter_hints: false, type_hints: false, chaining_hints: true, max_length: None}).unwrap(), @r###"
1334 [
1335 InlayHint {
1336 range: 246..283,
1337 kind: ChainingHint,
1338 label: "B<X<i32, bool>>",
1339 },
1340 InlayHint {
1341 range: 246..265,
1342 kind: ChainingHint,
1343 label: "A<X<i32, bool>>",
1344 },
1345 ]
1346 "###);
1347 } 917 }
1348} 918}