aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_ide')
-rw-r--r--crates/ra_ide/src/inlay_hints.rs309
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
3use hir::{HirDisplay, SourceAnalyzer, SourceBinder}; 3use hir::{Function, HirDisplay, SourceAnalyzer, SourceBinder};
4use once_cell::unsync::Lazy; 4use once_cell::unsync::Lazy;
5use ra_ide_db::RootDatabase; 5use ra_ide_db::RootDatabase;
6use ra_prof::profile; 6use ra_prof::profile;
7use ra_syntax::{ 7use 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
12use crate::{FileId, FunctionSignature}; 12use 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
83fn 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
98fn get_param_name_hints( 100fn 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
163fn 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
192fn 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)]
234mod tests { 173mod 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",