diff options
-rw-r--r-- | crates/ra_ide/src/inlay_hints.rs | 309 |
1 files changed, 174 insertions, 135 deletions
diff --git a/crates/ra_ide/src/inlay_hints.rs b/crates/ra_ide/src/inlay_hints.rs index 2ae97e65f..8d1c447ef 100644 --- a/crates/ra_ide/src/inlay_hints.rs +++ b/crates/ra_ide/src/inlay_hints.rs | |||
@@ -1,12 +1,12 @@ | |||
1 | //! FIXME: write short doc here | 1 | //! FIXME: write short doc here |
2 | 2 | ||
3 | use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; | 3 | use hir::{Function, HirDisplay, SourceAnalyzer, SourceBinder}; |
4 | use once_cell::unsync::Lazy; | 4 | use once_cell::unsync::Lazy; |
5 | use ra_ide_db::RootDatabase; | 5 | use ra_ide_db::RootDatabase; |
6 | use ra_prof::profile; | 6 | use ra_prof::profile; |
7 | use ra_syntax::{ | 7 | use ra_syntax::{ |
8 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, | 8 | ast::{self, ArgListOwner, AstNode, TypeAscriptionOwner}, |
9 | match_ast, SmolStr, SourceFile, SyntaxKind, SyntaxNode, TextRange, | 9 | match_ast, SmolStr, SourceFile, SyntaxNode, TextRange, |
10 | }; | 10 | }; |
11 | 11 | ||
12 | use crate::{FileId, FunctionSignature}; | 12 | use crate::{FileId, FunctionSignature}; |
@@ -50,51 +50,53 @@ fn get_inlay_hints( | |||
50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); | 50 | let analyzer = Lazy::new(move || sb.analyze(hir::InFile::new(file_id.into(), node), None)); |
51 | match_ast! { | 51 | match_ast! { |
52 | match node { | 52 | match node { |
53 | ast::LetStmt(it) => { | ||
54 | if it.ascribed_type().is_some() { | ||
55 | return None; | ||
56 | } | ||
57 | let pat = it.pat()?; | ||
58 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); | ||
59 | }, | ||
60 | ast::LambdaExpr(it) => { | ||
61 | it.param_list().map(|param_list| { | ||
62 | param_list | ||
63 | .params() | ||
64 | .filter(|closure_param| closure_param.ascribed_type().is_none()) | ||
65 | .filter_map(|closure_param| closure_param.pat()) | ||
66 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, false, max_inlay_hint_length)) | ||
67 | }); | ||
68 | }, | ||
69 | ast::ForExpr(it) => { | ||
70 | let pat = it.pat()?; | ||
71 | get_pat_type_hints(acc, db, &analyzer, pat, false, max_inlay_hint_length); | ||
72 | }, | ||
73 | ast::IfExpr(it) => { | ||
74 | let pat = it.condition()?.pat()?; | ||
75 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); | ||
76 | }, | ||
77 | ast::WhileExpr(it) => { | ||
78 | let pat = it.condition()?.pat()?; | ||
79 | get_pat_type_hints(acc, db, &analyzer, pat, true, max_inlay_hint_length); | ||
80 | }, | ||
81 | ast::MatchArmList(it) => { | ||
82 | it.arms() | ||
83 | .filter_map(|match_arm| match_arm.pat()) | ||
84 | .for_each(|root_pat| get_pat_type_hints(acc, db, &analyzer, root_pat, true, max_inlay_hint_length)); | ||
85 | }, | ||
86 | ast::CallExpr(it) => { | 53 | ast::CallExpr(it) => { |
87 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 54 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
88 | }, | 55 | }, |
89 | ast::MethodCallExpr(it) => { | 56 | ast::MethodCallExpr(it) => { |
90 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); | 57 | get_param_name_hints(acc, db, &analyzer, ast::Expr::from(it)); |
91 | }, | 58 | }, |
59 | ast::BindPat(it) => { | ||
60 | if should_not_display_type_hint(&it) { | ||
61 | return None; | ||
62 | } | ||
63 | let pat = ast::Pat::from(it); | ||
64 | let ty = analyzer.type_of_pat(db, &pat)?; | ||
65 | if ty.is_unknown() { | ||
66 | return None; | ||
67 | } | ||
68 | |||
69 | acc.push( | ||
70 | InlayHint { | ||
71 | range: pat.syntax().text_range(), | ||
72 | kind: InlayKind::TypeHint, | ||
73 | label: ty.display_truncated(db, max_inlay_hint_length).to_string().into(), | ||
74 | } | ||
75 | ); | ||
76 | }, | ||
92 | _ => (), | 77 | _ => (), |
93 | } | 78 | } |
94 | }; | 79 | }; |
95 | Some(()) | 80 | Some(()) |
96 | } | 81 | } |
97 | 82 | ||
83 | fn should_not_display_type_hint(bind_pat: &ast::BindPat) -> bool { | ||
84 | for node in bind_pat.syntax().ancestors() { | ||
85 | match_ast! { | ||
86 | match node { | ||
87 | ast::LetStmt(it) => { | ||
88 | return it.ascribed_type().is_some() | ||
89 | }, | ||
90 | ast::Param(it) => { | ||
91 | return it.ascribed_type().is_some() | ||
92 | }, | ||
93 | _ => (), | ||
94 | } | ||
95 | } | ||
96 | } | ||
97 | false | ||
98 | } | ||
99 | |||
98 | fn get_param_name_hints( | 100 | fn get_param_name_hints( |
99 | acc: &mut Vec<InlayHint>, | 101 | acc: &mut Vec<InlayHint>, |
100 | db: &RootDatabase, | 102 | db: &RootDatabase, |
@@ -105,28 +107,33 @@ fn get_param_name_hints( | |||
105 | ast::Expr::CallExpr(expr) => expr.arg_list()?.args(), | 107 | ast::Expr::CallExpr(expr) => expr.arg_list()?.args(), |
106 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), | 108 | ast::Expr::MethodCallExpr(expr) => expr.arg_list()?.args(), |
107 | _ => return None, | 109 | _ => return None, |
110 | } | ||
111 | .into_iter() | ||
112 | // we need args len to determine whether to skip or not the &self parameter | ||
113 | .collect::<Vec<_>>(); | ||
114 | |||
115 | let (has_self_param, fn_signature) = get_fn_signature(db, analyzer, &expr)?; | ||
116 | let parameters = if has_self_param && fn_signature.parameter_names.len() > args.len() { | ||
117 | fn_signature.parameter_names.into_iter().skip(1) | ||
118 | } else { | ||
119 | fn_signature.parameter_names.into_iter().skip(0) | ||
108 | }; | 120 | }; |
109 | 121 | ||
110 | let mut parameters = get_fn_signature(db, analyzer, &expr)?.parameter_names.into_iter(); | 122 | let hints = |
111 | 123 | parameters | |
112 | if let ast::Expr::MethodCallExpr(_) = &expr { | 124 | .zip(args) |
113 | parameters.next(); | 125 | .filter_map(|(param, arg)| { |
114 | }; | 126 | if !param.is_empty() { |
115 | 127 | Some((arg.syntax().text_range(), param)) | |
116 | let hints = parameters | 128 | } else { |
117 | .zip(args) | 129 | None |
118 | .filter_map(|(param, arg)| { | 130 | } |
119 | if arg.syntax().kind() == SyntaxKind::LITERAL && !param.is_empty() { | 131 | }) |
120 | Some((arg.syntax().text_range(), param)) | 132 | .map(|(range, param_name)| InlayHint { |
121 | } else { | 133 | range, |
122 | None | 134 | kind: InlayKind::ParameterHint, |
123 | } | 135 | label: param_name.into(), |
124 | }) | 136 | }); |
125 | .map(|(range, param_name)| InlayHint { | ||
126 | range, | ||
127 | kind: InlayKind::ParameterHint, | ||
128 | label: param_name.into(), | ||
129 | }); | ||
130 | 137 | ||
131 | acc.extend(hints); | 138 | acc.extend(hints); |
132 | Some(()) | 139 | Some(()) |
@@ -136,100 +143,32 @@ fn get_fn_signature( | |||
136 | db: &RootDatabase, | 143 | db: &RootDatabase, |
137 | analyzer: &SourceAnalyzer, | 144 | analyzer: &SourceAnalyzer, |
138 | expr: &ast::Expr, | 145 | expr: &ast::Expr, |
139 | ) -> Option<FunctionSignature> { | 146 | ) -> Option<(bool, FunctionSignature)> { |
140 | match expr { | 147 | match expr { |
141 | ast::Expr::CallExpr(expr) => { | 148 | ast::Expr::CallExpr(expr) => { |
142 | // FIXME: Type::as_callable is broken for closures | 149 | // FIXME: Type::as_callable is broken for closures |
143 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; | 150 | let callable_def = analyzer.type_of(db, &expr.expr()?)?.as_callable()?; |
144 | match callable_def { | 151 | match callable_def { |
145 | hir::CallableDef::FunctionId(it) => { | 152 | hir::CallableDef::FunctionId(it) => { |
146 | let fn_def = it.into(); | 153 | let fn_def: Function = it.into(); |
147 | Some(FunctionSignature::from_hir(db, fn_def)) | 154 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) |
148 | } | 155 | } |
149 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()), | 156 | hir::CallableDef::StructId(it) => FunctionSignature::from_struct(db, it.into()) |
157 | .map(|signature| (false, signature)), | ||
150 | hir::CallableDef::EnumVariantId(it) => { | 158 | hir::CallableDef::EnumVariantId(it) => { |
151 | FunctionSignature::from_enum_variant(db, it.into()) | 159 | FunctionSignature::from_enum_variant(db, it.into()) |
160 | .map(|signature| (false, signature)) | ||
152 | } | 161 | } |
153 | } | 162 | } |
154 | } | 163 | } |
155 | ast::Expr::MethodCallExpr(expr) => { | 164 | ast::Expr::MethodCallExpr(expr) => { |
156 | let fn_def = analyzer.resolve_method_call(&expr)?; | 165 | let fn_def = analyzer.resolve_method_call(&expr)?; |
157 | Some(FunctionSignature::from_hir(db, fn_def)) | 166 | Some((fn_def.has_self_param(db), FunctionSignature::from_hir(db, fn_def))) |
158 | } | 167 | } |
159 | _ => None, | 168 | _ => None, |
160 | } | 169 | } |
161 | } | 170 | } |
162 | 171 | ||
163 | fn get_pat_type_hints( | ||
164 | acc: &mut Vec<InlayHint>, | ||
165 | db: &RootDatabase, | ||
166 | analyzer: &SourceAnalyzer, | ||
167 | root_pat: ast::Pat, | ||
168 | skip_root_pat_hint: bool, | ||
169 | max_inlay_hint_length: Option<usize>, | ||
170 | ) { | ||
171 | let original_pat = &root_pat.clone(); | ||
172 | |||
173 | let hints = get_leaf_pats(root_pat) | ||
174 | .into_iter() | ||
175 | .filter(|pat| !skip_root_pat_hint || pat != original_pat) | ||
176 | .filter_map(|pat| { | ||
177 | let ty = analyzer.type_of_pat(db, &pat)?; | ||
178 | if ty.is_unknown() { | ||
179 | return None; | ||
180 | } | ||
181 | Some((pat.syntax().text_range(), ty)) | ||
182 | }) | ||
183 | .map(|(range, pat_type)| InlayHint { | ||
184 | range, | ||
185 | kind: InlayKind::TypeHint, | ||
186 | label: pat_type.display_truncated(db, max_inlay_hint_length).to_string().into(), | ||
187 | }); | ||
188 | |||
189 | acc.extend(hints); | ||
190 | } | ||
191 | |||
192 | fn get_leaf_pats(root_pat: ast::Pat) -> Vec<ast::Pat> { | ||
193 | let mut pats_to_process = std::collections::VecDeque::<ast::Pat>::new(); | ||
194 | pats_to_process.push_back(root_pat); | ||
195 | |||
196 | let mut leaf_pats = Vec::new(); | ||
197 | |||
198 | while let Some(maybe_leaf_pat) = pats_to_process.pop_front() { | ||
199 | match &maybe_leaf_pat { | ||
200 | ast::Pat::BindPat(bind_pat) => match bind_pat.pat() { | ||
201 | Some(pat) => pats_to_process.push_back(pat), | ||
202 | _ => leaf_pats.push(maybe_leaf_pat), | ||
203 | }, | ||
204 | ast::Pat::OrPat(ref_pat) => pats_to_process.extend(ref_pat.pats()), | ||
205 | ast::Pat::TuplePat(tuple_pat) => pats_to_process.extend(tuple_pat.args()), | ||
206 | ast::Pat::RecordPat(record_pat) => { | ||
207 | if let Some(pat_list) = record_pat.record_field_pat_list() { | ||
208 | pats_to_process.extend( | ||
209 | pat_list | ||
210 | .record_field_pats() | ||
211 | .filter_map(|record_field_pat| { | ||
212 | record_field_pat | ||
213 | .pat() | ||
214 | .filter(|pat| pat.syntax().kind() != SyntaxKind::BIND_PAT) | ||
215 | }) | ||
216 | .chain(pat_list.bind_pats().map(|bind_pat| { | ||
217 | bind_pat.pat().unwrap_or_else(|| ast::Pat::from(bind_pat)) | ||
218 | })), | ||
219 | ); | ||
220 | } | ||
221 | } | ||
222 | ast::Pat::TupleStructPat(tuple_struct_pat) => { | ||
223 | pats_to_process.extend(tuple_struct_pat.args()) | ||
224 | } | ||
225 | ast::Pat::ParenPat(inner_pat) => pats_to_process.extend(inner_pat.pat()), | ||
226 | ast::Pat::RefPat(ref_pat) => pats_to_process.extend(ref_pat.pat()), | ||
227 | _ => (), | ||
228 | } | ||
229 | } | ||
230 | leaf_pats | ||
231 | } | ||
232 | |||
233 | #[cfg(test)] | 172 | #[cfg(test)] |
234 | mod tests { | 173 | mod tests { |
235 | use insta::assert_debug_snapshot; | 174 | use insta::assert_debug_snapshot; |
@@ -346,11 +285,6 @@ fn main() { | |||
346 | label: "i32", | 285 | label: "i32", |
347 | }, | 286 | }, |
348 | InlayHint { | 287 | InlayHint { |
349 | range: [584; 585), | ||
350 | kind: TypeHint, | ||
351 | label: "i32", | ||
352 | }, | ||
353 | InlayHint { | ||
354 | range: [577; 578), | 288 | range: [577; 578), |
355 | kind: TypeHint, | 289 | kind: TypeHint, |
356 | label: "f64", | 290 | label: "f64", |
@@ -361,6 +295,11 @@ fn main() { | |||
361 | label: "f64", | 295 | label: "f64", |
362 | }, | 296 | }, |
363 | InlayHint { | 297 | InlayHint { |
298 | range: [584; 585), | ||
299 | kind: TypeHint, | ||
300 | label: "i32", | ||
301 | }, | ||
302 | InlayHint { | ||
364 | range: [627; 628), | 303 | range: [627; 628), |
365 | kind: TypeHint, | 304 | kind: TypeHint, |
366 | label: "i32", | 305 | label: "i32", |
@@ -508,6 +447,11 @@ fn main() { | |||
508 | label: "CustomOption<Test>", | 447 | label: "CustomOption<Test>", |
509 | }, | 448 | }, |
510 | InlayHint { | 449 | InlayHint { |
450 | range: [287; 291), | ||
451 | kind: TypeHint, | ||
452 | label: "&CustomOption<Test>", | ||
453 | }, | ||
454 | InlayHint { | ||
511 | range: [334; 338), | 455 | range: [334; 338), |
512 | kind: TypeHint, | 456 | kind: TypeHint, |
513 | label: "&Test", | 457 | label: "&Test", |
@@ -523,10 +467,35 @@ fn main() { | |||
523 | label: "&u8", | 467 | label: "&u8", |
524 | }, | 468 | }, |
525 | InlayHint { | 469 | InlayHint { |
470 | range: [449; 450), | ||
471 | kind: TypeHint, | ||
472 | label: "&CustomOption<u32>", | ||
473 | }, | ||
474 | InlayHint { | ||
475 | range: [455; 456), | ||
476 | kind: TypeHint, | ||
477 | label: "&u8", | ||
478 | }, | ||
479 | InlayHint { | ||
526 | range: [531; 532), | 480 | range: [531; 532), |
527 | kind: TypeHint, | 481 | kind: TypeHint, |
528 | label: "&u32", | 482 | label: "&u32", |
529 | }, | 483 | }, |
484 | InlayHint { | ||
485 | range: [538; 539), | ||
486 | kind: TypeHint, | ||
487 | label: "&u8", | ||
488 | }, | ||
489 | InlayHint { | ||
490 | range: [618; 619), | ||
491 | kind: TypeHint, | ||
492 | label: "&u8", | ||
493 | }, | ||
494 | InlayHint { | ||
495 | range: [675; 676), | ||
496 | kind: TypeHint, | ||
497 | label: "&u8", | ||
498 | }, | ||
530 | ] | 499 | ] |
531 | "### | 500 | "### |
532 | ); | 501 | ); |
@@ -571,6 +540,11 @@ fn main() { | |||
571 | label: "CustomOption<Test>", | 540 | label: "CustomOption<Test>", |
572 | }, | 541 | }, |
573 | InlayHint { | 542 | InlayHint { |
543 | range: [293; 297), | ||
544 | kind: TypeHint, | ||
545 | label: "&CustomOption<Test>", | ||
546 | }, | ||
547 | InlayHint { | ||
574 | range: [343; 347), | 548 | range: [343; 347), |
575 | kind: TypeHint, | 549 | kind: TypeHint, |
576 | label: "&Test", | 550 | label: "&Test", |
@@ -586,10 +560,35 @@ fn main() { | |||
586 | label: "&u8", | 560 | label: "&u8", |
587 | }, | 561 | }, |
588 | InlayHint { | 562 | InlayHint { |
563 | range: [464; 465), | ||
564 | kind: TypeHint, | ||
565 | label: "&CustomOption<u32>", | ||
566 | }, | ||
567 | InlayHint { | ||
568 | range: [470; 471), | ||
569 | kind: TypeHint, | ||
570 | label: "&u8", | ||
571 | }, | ||
572 | InlayHint { | ||
589 | range: [549; 550), | 573 | range: [549; 550), |
590 | kind: TypeHint, | 574 | kind: TypeHint, |
591 | label: "&u32", | 575 | label: "&u32", |
592 | }, | 576 | }, |
577 | InlayHint { | ||
578 | range: [556; 557), | ||
579 | kind: TypeHint, | ||
580 | label: "&u8", | ||
581 | }, | ||
582 | InlayHint { | ||
583 | range: [639; 640), | ||
584 | kind: TypeHint, | ||
585 | label: "&u8", | ||
586 | }, | ||
587 | InlayHint { | ||
588 | range: [699; 700), | ||
589 | kind: TypeHint, | ||
590 | label: "&u8", | ||
591 | }, | ||
593 | ] | 592 | ] |
594 | "### | 593 | "### |
595 | ); | 594 | ); |
@@ -629,6 +628,11 @@ fn main() { | |||
629 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" | 628 | assert_debug_snapshot!(analysis.inlay_hints(file_id, None).unwrap(), @r###" |
630 | [ | 629 | [ |
631 | InlayHint { | 630 | InlayHint { |
631 | range: [272; 276), | ||
632 | kind: TypeHint, | ||
633 | label: "CustomOption<Test>", | ||
634 | }, | ||
635 | InlayHint { | ||
632 | range: [311; 315), | 636 | range: [311; 315), |
633 | kind: TypeHint, | 637 | kind: TypeHint, |
634 | label: "Test", | 638 | label: "Test", |
@@ -644,10 +648,35 @@ fn main() { | |||
644 | label: "u8", | 648 | label: "u8", |
645 | }, | 649 | }, |
646 | InlayHint { | 650 | InlayHint { |
651 | range: [410; 411), | ||
652 | kind: TypeHint, | ||
653 | label: "CustomOption<u32>", | ||
654 | }, | ||
655 | InlayHint { | ||
656 | range: [416; 417), | ||
657 | kind: TypeHint, | ||
658 | label: "u8", | ||
659 | }, | ||
660 | InlayHint { | ||
647 | range: [484; 485), | 661 | range: [484; 485), |
648 | kind: TypeHint, | 662 | kind: TypeHint, |
649 | label: "u32", | 663 | label: "u32", |
650 | }, | 664 | }, |
665 | InlayHint { | ||
666 | range: [491; 492), | ||
667 | kind: TypeHint, | ||
668 | label: "u8", | ||
669 | }, | ||
670 | InlayHint { | ||
671 | range: [563; 564), | ||
672 | kind: TypeHint, | ||
673 | label: "u8", | ||
674 | }, | ||
675 | InlayHint { | ||
676 | range: [612; 613), | ||
677 | kind: TypeHint, | ||
678 | label: "u8", | ||
679 | }, | ||
651 | ] | 680 | ] |
652 | "### | 681 | "### |
653 | ); | 682 | ); |
@@ -738,11 +767,21 @@ fn main() { | |||
738 | label: "msg", | 767 | label: "msg", |
739 | }, | 768 | }, |
740 | InlayHint { | 769 | InlayHint { |
770 | range: [277; 288), | ||
771 | kind: ParameterHint, | ||
772 | label: "last", | ||
773 | }, | ||
774 | InlayHint { | ||
741 | range: [331; 334), | 775 | range: [331; 334), |
742 | kind: ParameterHint, | 776 | kind: ParameterHint, |
743 | label: "param", | 777 | label: "param", |
744 | }, | 778 | }, |
745 | InlayHint { | 779 | InlayHint { |
780 | range: [354; 356), | ||
781 | kind: ParameterHint, | ||
782 | label: "&self", | ||
783 | }, | ||
784 | InlayHint { | ||
746 | range: [358; 362), | 785 | range: [358; 362), |
747 | kind: ParameterHint, | 786 | kind: ParameterHint, |
748 | label: "param", | 787 | label: "param", |