aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_assists/src/assists
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_assists/src/assists')
-rw-r--r--crates/ra_assists/src/assists/add_explicit_type.rs54
1 files changed, 48 insertions, 6 deletions
diff --git a/crates/ra_assists/src/assists/add_explicit_type.rs b/crates/ra_assists/src/assists/add_explicit_type.rs
index 38a351a54..2443d5541 100644
--- a/crates/ra_assists/src/assists/add_explicit_type.rs
+++ b/crates/ra_assists/src/assists/add_explicit_type.rs
@@ -1,7 +1,7 @@
1use hir::{db::HirDatabase, HirDisplay}; 1use hir::{db::HirDatabase, HirDisplay};
2use ra_syntax::{ 2use ra_syntax::{
3 ast::{self, AstNode, LetStmt, NameOwner}, 3 ast::{self, AstNode, LetStmt, NameOwner, TypeAscriptionOwner},
4 TextRange, T, 4 TextRange,
5}; 5};
6 6
7use crate::{Assist, AssistCtx, AssistId}; 7use crate::{Assist, AssistCtx, AssistId};
@@ -34,17 +34,21 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
34 // The binding must have a name 34 // The binding must have a name
35 let name = pat.name()?; 35 let name = pat.name()?;
36 let name_range = name.syntax().text_range(); 36 let name_range = name.syntax().text_range();
37 // Assist should only be applicable if cursor is between 'let' and '='
38 let stmt_range = stmt.syntax().text_range(); 37 let stmt_range = stmt.syntax().text_range();
39 let eq_range = stmt.eq_token()?.text_range(); 38 let eq_range = stmt.eq_token()?.text_range();
39 // Assist should only be applicable if cursor is between 'let' and '='
40 let let_range = TextRange::from_to(stmt_range.start(), eq_range.start()); 40 let let_range = TextRange::from_to(stmt_range.start(), eq_range.start());
41 let cursor_in_range = ctx.frange.range.is_subrange(&let_range); 41 let cursor_in_range = ctx.frange.range.is_subrange(&let_range);
42 if !cursor_in_range { 42 if !cursor_in_range {
43 return None; 43 return None;
44 } 44 }
45 // Assist not applicable if the type has already been specified 45 // Assist not applicable if the type has already been specified
46 if stmt.syntax().children_with_tokens().any(|child| child.kind() == T![:]) { 46 // and it has no placeholders
47 return None; 47 let ascribed_ty = stmt.ascribed_type();
48 if let Some(ref ty) = ascribed_ty {
49 if ty.syntax().descendants().find_map(ast::PlaceholderType::cast).is_none() {
50 return None;
51 }
48 } 52 }
49 // Infer type 53 // Infer type
50 let db = ctx.db; 54 let db = ctx.db;
@@ -60,7 +64,11 @@ pub(crate) fn add_explicit_type(ctx: AssistCtx<impl HirDatabase>) -> Option<Assi
60 format!("Insert explicit type '{}'", ty.display(db)), 64 format!("Insert explicit type '{}'", ty.display(db)),
61 |edit| { 65 |edit| {
62 edit.target(pat_range); 66 edit.target(pat_range);
63 edit.insert(name_range.end(), format!(": {}", ty.display(db))); 67 if let Some(ascribed_ty) = ascribed_ty {
68 edit.replace(ascribed_ty.syntax().text_range(), format!("{}", ty.display(db)));
69 } else {
70 edit.insert(name_range.end(), format!(": {}", ty.display(db)));
71 }
64 }, 72 },
65 ) 73 )
66} 74}
@@ -86,6 +94,40 @@ mod tests {
86 } 94 }
87 95
88 #[test] 96 #[test]
97 fn add_explicit_type_works_for_underscore() {
98 check_assist(
99 add_explicit_type,
100 "fn f() { let a<|>: _ = 1; }",
101 "fn f() { let a<|>: i32 = 1; }",
102 );
103 }
104
105 #[test]
106 fn add_explicit_type_works_for_nested_underscore() {
107 check_assist(
108 add_explicit_type,
109 r#"
110 enum Option<T> {
111 Some(T),
112 None
113 }
114
115 fn f() {
116 let a<|>: Option<_> = Option::Some(1);
117 }"#,
118 r#"
119 enum Option<T> {
120 Some(T),
121 None
122 }
123
124 fn f() {
125 let a<|>: Option<i32> = Option::Some(1);
126 }"#,
127 );
128 }
129
130 #[test]
89 fn add_explicit_type_works_for_macro_call() { 131 fn add_explicit_type_works_for_macro_call() {
90 check_assist( 132 check_assist(
91 add_explicit_type, 133 add_explicit_type,