aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_assists/src/add_explicit_type.rs93
-rw-r--r--crates/ra_assists/src/lib.rs2
-rw-r--r--docs/user/features.md32
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 @@
1use hir::{
2 HirDisplay, Ty,
3 db::HirDatabase,
4 source_binder::function_from_child_node,
5};
6use ra_syntax::{
7 SyntaxKind,
8 ast::{LetStmt, PatKind, NameOwner, AstNode}
9};
10
11use crate::{AssistCtx, Assist, AssistId};
12
13/// Add explicit type assist.
14pub(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
51fn 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)]
60mod 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
88mod add_derive; 88mod add_derive;
89mod add_explicit_type;
89mod add_impl; 90mod add_impl;
90mod flip_comma; 91mod flip_comma;
91mod flip_binexpr; 92mod flip_binexpr;
@@ -103,6 +104,7 @@ mod add_missing_impl_members;
103fn all_assists<DB: HirDatabase>() -> &'static [fn(AssistCtx<DB>) -> Option<Assist>] { 104fn 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:
335use algo:<|>:visitor::{Visitor, visit}; 335use algo:<|>:visitor::{Visitor, visit};
336//after: 336// after:
337use algo::{<|>visitor::{Visitor, visit}}; 337use algo::{<|>visitor::{Visitor, visit}};
338``` 338```
339 339
340- Flip binary expression
341
342```rust
343// before:
344fn foo() {
345 if 1 <<|> 2 {
346 println!("Who would have thought?");
347 }
348}
349// after:
350fn foo() {
351 if 2 ><|> 1 {
352 println!("Who would have thought?");
353 }
354}
355```
356
357- Add explicit type
358
359```rust
360// before:
361fn foo() {
362 let t<|> = (&2, Some(1));
363}
364// after:
365fn foo() {
366 let t<|>: (&i32, Option<i32>) = (&2, Some(1));
367}
368```
369
340### Magic Completions 370### Magic Completions
341 371
342In addition to usual reference completion, rust-analyzer provides some ✨magic✨ 372In addition to usual reference completion, rust-analyzer provides some ✨magic✨