aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorConrad Ludgate <[email protected]>2021-03-01 10:51:47 +0000
committerConrad Ludgate <[email protected]>2021-03-02 14:30:10 +0000
commit705712993ffe24898e3c1fe006e1108b7d02d6bc (patch)
treee3f40b0432e0dff4e1f53f37851c56ac9a94fc7e
parentf8152171bbe160c4273d692d42c06eb7c6d66e1a (diff)
feat: add type ascription assist
-rw-r--r--crates/ide_assists/src/handlers/add_type_ascription.rs198
-rw-r--r--crates/ide_assists/src/lib.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs19
-rw-r--r--xtask/src/tidy.rs1
4 files changed, 220 insertions, 0 deletions
diff --git a/crates/ide_assists/src/handlers/add_type_ascription.rs b/crates/ide_assists/src/handlers/add_type_ascription.rs
new file mode 100644
index 000000000..e9dc37150
--- /dev/null
+++ b/crates/ide_assists/src/handlers/add_type_ascription.rs
@@ -0,0 +1,198 @@
1use ide_db::defs::{Definition, NameRefClass};
2use syntax::{ast, AstNode, SyntaxKind, T};
3use test_utils::mark;
4
5use crate::{
6 assist_context::{AssistContext, Assists},
7 AssistId, AssistKind,
8};
9
10// Assist: add_type_ascription
11//
12// Adds `: _` before the assignment operator to prompt the user for a type
13//
14// ```
15// fn make<T>() -> T { todo!() }
16// fn main() {
17// let x = make$0();
18// }
19// ```
20// ->
21// ```
22// fn make<T>() -> T { todo!() }
23// fn main() {
24// let x: ${0:_} = make();
25// }
26// ```
27pub(crate) fn add_type_ascription(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
28 let let_stmt = ctx.find_node_at_offset::<ast::LetStmt>()?;
29 if let_stmt.colon_token().is_some() {
30 mark::hit!(add_type_ascription_already_typed);
31 return None
32 }
33
34 let ident = ctx.find_token_syntax_at_offset(SyntaxKind::IDENT).or_else(|| {
35 let arg_list = ctx.find_node_at_offset::<ast::ArgList>()?;
36 if arg_list.args().count() > 0 {
37 return None;
38 }
39 mark::hit!(add_type_ascription_after_call);
40 arg_list.l_paren_token()?.prev_token().filter(|it| it.kind() == SyntaxKind::IDENT)
41 })?;
42 let next_token = ident.next_token()?;
43 if next_token.kind() == T![::] {
44 mark::hit!(add_type_ascription_turbofished);
45 return None;
46 }
47 let name_ref = ast::NameRef::cast(ident.parent())?;
48 let def = match NameRefClass::classify(&ctx.sema, &name_ref)? {
49 NameRefClass::Definition(def) => def,
50 NameRefClass::ExternCrate(_) | NameRefClass::FieldShorthand { .. } => return None,
51 };
52 let fun = match def {
53 Definition::ModuleDef(hir::ModuleDef::Function(it)) => it,
54 _ => return None,
55 };
56 let generics = hir::GenericDef::Function(fun).params(ctx.sema.db);
57 if generics.is_empty() {
58 mark::hit!(add_type_ascription_non_generic);
59 return None;
60 }
61 let pat = let_stmt.pat()?.syntax().last_token()?.text_range().end();
62 acc.add(
63 AssistId("add_type_ascription", AssistKind::RefactorRewrite),
64 "Add `: _` before assignment operator",
65 ident.text_range(),
66 |builder| match ctx.config.snippet_cap {
67 Some(cap) => builder.insert_snippet(cap, pat, ": ${0:_}"),
68 None => builder.insert(pat, ": _"),
69 },
70 )
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::tests::{check_assist, check_assist_not_applicable};
76
77 use super::*;
78 use test_utils::mark;
79
80 #[test]
81 fn add_type_ascription_function() {
82 check_assist(
83 add_type_ascription,
84 r#"
85fn make<T>() -> T {}
86fn main() {
87 let x = make$0();
88}
89"#,
90 r#"
91fn make<T>() -> T {}
92fn main() {
93 let x: ${0:_} = make();
94}
95"#,
96 );
97 }
98
99 #[test]
100 fn add_type_ascription_after_call() {
101 mark::check!(add_type_ascription_after_call);
102 check_assist(
103 add_type_ascription,
104 r#"
105fn make<T>() -> T {}
106fn main() {
107 let x = make()$0;
108}
109"#,
110 r#"
111fn make<T>() -> T {}
112fn main() {
113 let x: ${0:_} = make();
114}
115"#,
116 );
117 }
118
119 #[test]
120 fn add_type_ascription_method() {
121 check_assist(
122 add_type_ascription,
123 r#"
124struct S;
125impl S {
126 fn make<T>(&self) -> T {}
127}
128fn main() {
129 let x = S.make$0();
130}
131"#,
132 r#"
133struct S;
134impl S {
135 fn make<T>(&self) -> T {}
136}
137fn main() {
138 let x: ${0:_} = S.make();
139}
140"#,
141 );
142 }
143
144 #[test]
145 fn add_type_ascription_turbofished() {
146 mark::check!(add_type_ascription_turbofished);
147 check_assist_not_applicable(
148 add_type_ascription,
149 r#"
150fn make<T>() -> T {}
151fn main() {
152 let x = make$0::<()>();
153}
154"#,
155 );
156 }
157
158 #[test]
159 fn add_type_ascription_already_typed() {
160 mark::check!(add_type_ascription_already_typed);
161 check_assist_not_applicable(
162 add_type_ascription,
163 r#"
164fn make<T>() -> T {}
165fn main() {
166 let x: () = make$0();
167}
168"#,
169 );
170 }
171
172 #[test]
173 fn add_type_ascription_non_generic() {
174 mark::check!(add_type_ascription_non_generic);
175 check_assist_not_applicable(
176 add_type_ascription,
177 r#"
178fn make() -> () {}
179fn main() {
180 let x = make$0();
181}
182"#,
183 );
184 }
185
186 #[test]
187 fn add_type_ascription_no_let() {
188 check_assist_not_applicable(
189 add_type_ascription,
190 r#"
191fn make<T>() -> T {}
192fn main() {
193 make$0();
194}
195"#,
196 );
197 }
198}
diff --git a/crates/ide_assists/src/lib.rs b/crates/ide_assists/src/lib.rs
index 9c8148462..0248cb9c0 100644
--- a/crates/ide_assists/src/lib.rs
+++ b/crates/ide_assists/src/lib.rs
@@ -111,6 +111,7 @@ mod handlers {
111 mod add_lifetime_to_type; 111 mod add_lifetime_to_type;
112 mod add_missing_impl_members; 112 mod add_missing_impl_members;
113 mod add_turbo_fish; 113 mod add_turbo_fish;
114 mod add_type_ascription;
114 mod apply_demorgan; 115 mod apply_demorgan;
115 mod auto_import; 116 mod auto_import;
116 mod change_visibility; 117 mod change_visibility;
@@ -175,6 +176,7 @@ mod handlers {
175 add_explicit_type::add_explicit_type, 176 add_explicit_type::add_explicit_type,
176 add_lifetime_to_type::add_lifetime_to_type, 177 add_lifetime_to_type::add_lifetime_to_type,
177 add_turbo_fish::add_turbo_fish, 178 add_turbo_fish::add_turbo_fish,
179 add_type_ascription::add_type_ascription,
178 apply_demorgan::apply_demorgan, 180 apply_demorgan::apply_demorgan,
179 auto_import::auto_import, 181 auto_import::auto_import,
180 change_visibility::change_visibility, 182 change_visibility::change_visibility,
diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs
index 4f007aa48..439ee8b22 100644
--- a/crates/ide_assists/src/tests/generated.rs
+++ b/crates/ide_assists/src/tests/generated.rs
@@ -142,6 +142,25 @@ fn main() {
142} 142}
143 143
144#[test] 144#[test]
145fn doctest_add_type_ascription() {
146 check_doc_test(
147 "add_type_ascription",
148 r#####"
149fn make<T>() -> T { todo!() }
150fn main() {
151 let x = make$0();
152}
153"#####,
154 r#####"
155fn make<T>() -> T { todo!() }
156fn main() {
157 let x: ${0:_} = make();
158}
159"#####,
160 )
161}
162
163#[test]
145fn doctest_apply_demorgan() { 164fn doctest_apply_demorgan() {
146 check_doc_test( 165 check_doc_test(
147 "apply_demorgan", 166 "apply_demorgan",
diff --git a/xtask/src/tidy.rs b/xtask/src/tidy.rs
index 349ca14d0..91f1ee217 100644
--- a/xtask/src/tidy.rs
+++ b/xtask/src/tidy.rs
@@ -277,6 +277,7 @@ fn check_todo(path: &Path, text: &str) {
277 "tests/tidy.rs", 277 "tests/tidy.rs",
278 // Some of our assists generate `todo!()`. 278 // Some of our assists generate `todo!()`.
279 "handlers/add_turbo_fish.rs", 279 "handlers/add_turbo_fish.rs",
280 "handlers/add_type_ascription.rs",
280 "handlers/generate_function.rs", 281 "handlers/generate_function.rs",
281 // To support generating `todo!()` in assists, we have `expr_todo()` in 282 // To support generating `todo!()` in assists, we have `expr_todo()` in
282 // `ast::make`. 283 // `ast::make`.