diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-09 20:16:28 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-04-09 20:16:28 +0100 |
commit | cc284dad30de4990516eeccf60f24e613fd78a2a (patch) | |
tree | ab1b62d318b44bec3706a30401d5b4213f8618a3 | |
parent | 2fc2d4373b2c4e96bebf320a84270eee3afe34aa (diff) | |
parent | c5f8f3b1f423781e09bb5f63e33d772ee59fab77 (diff) |
Merge #1122
1122: Add explicit type assist. r=matklad a=marcogroppo
This assist can be used to specify the explicit type in let statements. For example `let num = 1;` becomes `let num: i32 = 1;`.
The assist is applicable only if the inferred type is fully known.
Co-authored-by: Marco Groppo <[email protected]>
-rw-r--r-- | crates/ra_assists/src/add_explicit_type.rs | 93 | ||||
-rw-r--r-- | crates/ra_assists/src/lib.rs | 2 | ||||
-rw-r--r-- | docs/user/features.md | 32 |
3 files changed, 126 insertions, 1 deletions
diff --git a/crates/ra_assists/src/add_explicit_type.rs b/crates/ra_assists/src/add_explicit_type.rs new file mode 100644 index 000000000..1dc59bb87 --- /dev/null +++ b/crates/ra_assists/src/add_explicit_type.rs | |||
@@ -0,0 +1,93 @@ | |||
1 | use hir::{ | ||
2 | HirDisplay, Ty, | ||
3 | db::HirDatabase, | ||
4 | source_binder::function_from_child_node, | ||
5 | }; | ||
6 | use ra_syntax::{ | ||
7 | SyntaxKind, | ||
8 | ast::{LetStmt, PatKind, NameOwner, AstNode} | ||
9 | }; | ||
10 | |||
11 | use crate::{AssistCtx, Assist, AssistId}; | ||
12 | |||
13 | /// Add explicit type assist. | ||
14 | pub(crate) fn add_explicit_type(mut ctx: AssistCtx<impl HirDatabase>) -> Option<Assist> { | ||
15 | let stmt = ctx.node_at_offset::<LetStmt>()?; | ||
16 | let expr = stmt.initializer()?; | ||
17 | let pat = stmt.pat()?; | ||
18 | // Must be a binding | ||
19 | let pat = match pat.kind() { | ||
20 | PatKind::BindPat(bind_pat) => bind_pat, | ||
21 | _ => return None, | ||
22 | }; | ||
23 | let pat_range = pat.syntax().range(); | ||
24 | // The binding must have a name | ||
25 | let name = pat.name()?; | ||
26 | let name_range = name.syntax().range(); | ||
27 | // Assist not applicable if the type has already been specified | ||
28 | if stmt.syntax().children_with_tokens().any(|child| child.kind() == SyntaxKind::COLON) { | ||
29 | return None; | ||
30 | } | ||
31 | // Infer type | ||
32 | let db = ctx.db; | ||
33 | let func = function_from_child_node(db, ctx.frange.file_id, pat.syntax())?; | ||
34 | let inference_res = func.infer(db); | ||
35 | let source_map = func.body_source_map(db); | ||
36 | let expr_id = source_map.node_expr(expr.into())?; | ||
37 | let ty = inference_res[expr_id].clone(); | ||
38 | // Assist not applicable if the type is unknown | ||
39 | if is_unknown(&ty) { | ||
40 | return None; | ||
41 | } | ||
42 | |||
43 | ctx.add_action(AssistId("add_explicit_type"), "add explicit type", |edit| { | ||
44 | edit.target(pat_range); | ||
45 | edit.insert(name_range.end(), format!(": {}", ty.display(db))); | ||
46 | }); | ||
47 | ctx.build() | ||
48 | } | ||
49 | |||
50 | /// Returns true if any type parameter is unknown | ||
51 | fn is_unknown(ty: &Ty) -> bool { | ||
52 | match ty { | ||
53 | Ty::Unknown => true, | ||
54 | Ty::Apply(a_ty) => a_ty.parameters.iter().any(is_unknown), | ||
55 | _ => false, | ||
56 | } | ||
57 | } | ||
58 | |||
59 | #[cfg(test)] | ||
60 | mod tests { | ||
61 | use super::*; | ||
62 | |||
63 | use crate::helpers::{ check_assist, check_assist_target, check_assist_not_applicable }; | ||
64 | |||
65 | #[test] | ||
66 | fn add_explicit_type_target() { | ||
67 | check_assist_target(add_explicit_type, "fn f() { let a<|> = 1; }", "a"); | ||
68 | } | ||
69 | |||
70 | #[test] | ||
71 | fn add_explicit_type_works_for_simple_expr() { | ||
72 | check_assist( | ||
73 | add_explicit_type, | ||
74 | "fn f() { let a<|> = 1; }", | ||
75 | "fn f() { let a<|>: i32 = 1; }", | ||
76 | ); | ||
77 | } | ||
78 | |||
79 | #[test] | ||
80 | fn add_explicit_type_not_applicable_if_ty_not_inferred() { | ||
81 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|> = None; }"); | ||
82 | } | ||
83 | |||
84 | #[test] | ||
85 | fn add_explicit_type_not_applicable_if_ty_already_specified() { | ||
86 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: i32 = 1; }"); | ||
87 | } | ||
88 | |||
89 | #[test] | ||
90 | fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() { | ||
91 | check_assist_not_applicable(add_explicit_type, "fn f() { let a<|>: (i32, i32) = (3, 4); }"); | ||
92 | } | ||
93 | } | ||
diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index c1514f8e5..ded401b63 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs | |||
@@ -86,6 +86,7 @@ where | |||
86 | } | 86 | } |
87 | 87 | ||
88 | mod add_derive; | 88 | mod add_derive; |
89 | mod add_explicit_type; | ||
89 | mod add_impl; | 90 | mod add_impl; |
90 | mod flip_comma; | 91 | mod flip_comma; |
91 | mod flip_binexpr; | 92 | mod flip_binexpr; |
@@ -103,6 +104,7 @@ mod add_missing_impl_members; | |||
103 | fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { | 104 | fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { |
104 | &[ | 105 | &[ |
105 | add_derive::add_derive, | 106 | add_derive::add_derive, |
107 | add_explicit_type::add_explicit_type, | ||
106 | add_impl::add_impl, | 108 | add_impl::add_impl, |
107 | change_visibility::change_visibility, | 109 | change_visibility::change_visibility, |
108 | fill_match_arms::fill_match_arms, | 110 | fill_match_arms::fill_match_arms, |
diff --git a/docs/user/features.md b/docs/user/features.md index 3ac99eef1..09a7f5a43 100644 --- a/docs/user/features.md +++ b/docs/user/features.md | |||
@@ -333,10 +333,40 @@ impl VariantData { | |||
333 | ```rust | 333 | ```rust |
334 | // before: | 334 | // before: |
335 | use algo:<|>:visitor::{Visitor, visit}; | 335 | use algo:<|>:visitor::{Visitor, visit}; |
336 | //after: | 336 | // after: |
337 | use algo::{<|>visitor::{Visitor, visit}}; | 337 | use algo::{<|>visitor::{Visitor, visit}}; |
338 | ``` | 338 | ``` |
339 | 339 | ||
340 | - Flip binary expression | ||
341 | |||
342 | ```rust | ||
343 | // before: | ||
344 | fn foo() { | ||
345 | if 1 <<|> 2 { | ||
346 | println!("Who would have thought?"); | ||
347 | } | ||
348 | } | ||
349 | // after: | ||
350 | fn foo() { | ||
351 | if 2 ><|> 1 { | ||
352 | println!("Who would have thought?"); | ||
353 | } | ||
354 | } | ||
355 | ``` | ||
356 | |||
357 | - Add explicit type | ||
358 | |||
359 | ```rust | ||
360 | // before: | ||
361 | fn foo() { | ||
362 | let t<|> = (&2, Some(1)); | ||
363 | } | ||
364 | // after: | ||
365 | fn foo() { | ||
366 | let t<|>: (&i32, Option<i32>) = (&2, Some(1)); | ||
367 | } | ||
368 | ``` | ||
369 | |||
340 | ### Magic Completions | 370 | ### Magic Completions |
341 | 371 | ||
342 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ | 372 | In addition to usual reference completion, rust-analyzer provides some ✨magic✨ |