aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_ide/src
diff options
context:
space:
mode:
authorbors[bot] <26634292+bors[bot]@users.noreply.github.com>2020-02-23 09:25:40 +0000
committerGitHub <[email protected]>2020-02-23 09:25:40 +0000
commit1651ce0ebe1cf1d51541a04457f72595d669c967 (patch)
tree29529e62a16e8ac5db32706c6f44402bbc575c2b /crates/ra_ide/src
parent650586a83869d6769c3d0fb94ffd2ccfcbd97db5 (diff)
parentca4557ac524c104bff03d5f6dfc7bece7c919608 (diff)
Merge #3278
3278: Show more inlay hints r=matklad a=SomeoneToIgnore Closes https://github.com/rust-analyzer/rust-analyzer/issues/3273 As suggested in https://github.com/rust-analyzer/rust-analyzer/pull/1606#discussion_r308146363 , there is a simpler way to handle inlay hints after https://github.com/rust-analyzer/rust-analyzer/issues/1618 is closed. This PR uses the approach suggested (which results in more type hints for various bindings) and also adds more name hints for function parameters. Examples can be found in the issue: * https://github.com/rust-analyzer/rust-analyzer/issues/3273#issuecomment-589998051 * https://github.com/rust-analyzer/rust-analyzer/issues/3273#issuecomment-590002839 Co-authored-by: Kirill Bulatov <[email protected]>
Diffstat (limited to 'crates/ra_ide/src')
-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",