Skip to content

Commit 778e6e8

Browse files
committed
add_explicit_type is applicable for closure parameters
1 parent 1f81783 commit 778e6e8

File tree

3 files changed

+128
-81
lines changed

3 files changed

+128
-81
lines changed

crates/ide_assists/src/handlers/add_explicit_type.rs

Lines changed: 93 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
use hir::HirDisplay;
2-
use syntax::{
3-
ast::{self, AstNode, LetStmt},
4-
TextRange,
5-
};
2+
use syntax::ast::{self, AstNode, LetStmt, Param};
63

74
use crate::{AssistContext, AssistId, AssistKind, Assists};
85

@@ -22,40 +19,46 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
2219
// }
2320
// ```
2421
pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
25-
let let_stmt = ctx.find_node_at_offset::<LetStmt>()?;
26-
let module = ctx.sema.scope(let_stmt.syntax()).module()?;
27-
let expr = let_stmt.initializer()?;
28-
// Must be a binding
29-
let pat = match let_stmt.pat()? {
30-
ast::Pat::IdentPat(bind_pat) => bind_pat,
31-
_ => return None,
32-
};
33-
let pat_range = pat.syntax().text_range();
22+
let (ascribed_ty, expr, pat) = if let Some(let_stmt) = ctx.find_node_at_offset::<LetStmt>() {
23+
let cursor_in_range = {
24+
let eq_range = let_stmt.eq_token()?.text_range();
25+
ctx.offset() < eq_range.start()
26+
};
27+
if !cursor_in_range {
28+
cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
29+
return None;
30+
}
3431

35-
// Assist should only be applicable if cursor is between 'let' and '='
36-
let cursor_in_range = {
37-
let stmt_range = let_stmt.syntax().text_range();
38-
let eq_range = let_stmt.eq_token()?.text_range();
39-
let let_range = TextRange::new(stmt_range.start(), eq_range.start());
40-
let_range.contains_range(ctx.frange.range)
41-
};
42-
if !cursor_in_range {
43-
cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
32+
(let_stmt.ty(), let_stmt.initializer(), let_stmt.pat()?)
33+
} else if let Some(param) = ctx.find_node_at_offset::<Param>() {
34+
if param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_none() {
35+
cov_mark::hit!(add_explicit_type_not_applicable_in_fn_param);
36+
return None;
37+
}
38+
(param.ty(), None, param.pat()?)
39+
} else {
4440
return None;
45-
}
41+
};
42+
43+
let module = ctx.sema.scope(pat.syntax()).module()?;
44+
let pat_range = pat.syntax().text_range();
4645

47-
// Assist not applicable if the type has already been specified
48-
// and it has no placeholders
49-
let ascribed_ty = let_stmt.ty();
46+
// Don't enable the assist if there is a type ascription without any placeholders
5047
if let Some(ty) = &ascribed_ty {
51-
if ty.syntax().descendants().find_map(ast::InferType::cast).is_none() {
48+
let mut contains_infer_ty = false;
49+
ty.walk(&mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
50+
if !contains_infer_ty {
5251
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
5352
return None;
5453
}
5554
}
5655

57-
// Infer type
58-
let (ty, _) = ctx.sema.type_of_expr_with_coercion(&expr)?;
56+
let ty = match (pat, expr) {
57+
(ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr_with_coercion(&expr)?.0,
58+
(pat, _) => ctx.sema.type_of_pat(&pat)?,
59+
};
60+
61+
// Unresolved or unnameable types can't be annotated
5962
if ty.contains_unknown() || ty.is_closure() {
6063
cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
6164
return None;
@@ -89,7 +92,7 @@ mod tests {
8992
}
9093

9194
#[test]
92-
fn add_explicit_type_works_for_simple_expr() {
95+
fn add_explicit_type_simple() {
9396
check_assist(
9497
add_explicit_type,
9598
r#"fn f() { let a$0 = 1; }"#,
@@ -98,7 +101,7 @@ mod tests {
98101
}
99102

100103
#[test]
101-
fn add_explicit_type_works_for_underscore() {
104+
fn add_explicit_type_simple_on_infer_ty() {
102105
check_assist(
103106
add_explicit_type,
104107
r#"fn f() { let a$0: _ = 1; }"#,
@@ -107,19 +110,16 @@ mod tests {
107110
}
108111

109112
#[test]
110-
fn add_explicit_type_works_for_nested_underscore() {
113+
fn add_explicit_type_simple_nested_infer_ty() {
111114
check_assist(
112115
add_explicit_type,
113116
r#"
114-
enum Option<T> { Some(T), None }
115-
117+
//- minicore: option
116118
fn f() {
117119
let a$0: Option<_> = Option::Some(1);
118120
}
119121
"#,
120122
r#"
121-
enum Option<T> { Some(T), None }
122-
123123
fn f() {
124124
let a: Option<i32> = Option::Some(1);
125125
}
@@ -128,7 +128,7 @@ fn f() {
128128
}
129129

130130
#[test]
131-
fn add_explicit_type_works_for_macro_call() {
131+
fn add_explicit_type_macro_call_expr() {
132132
check_assist(
133133
add_explicit_type,
134134
r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
@@ -137,64 +137,31 @@ fn f() {
137137
}
138138

139139
#[test]
140-
fn add_explicit_type_works_for_macro_call_recursive() {
141-
check_assist(
142-
add_explicit_type,
143-
r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a$0 = v!(); }"#,
144-
r#"macro_rules! u { () => {0u64} } macro_rules! v { () => {u!()} } fn f() { let a: u64 = v!(); }"#,
145-
);
146-
}
147-
148-
#[test]
149-
fn add_explicit_type_not_applicable_if_ty_not_inferred() {
140+
fn add_explicit_type_not_applicable_unresolved() {
150141
cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
151142
check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
152143
}
153144

154145
#[test]
155-
fn add_explicit_type_not_applicable_if_ty_already_specified() {
156-
cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
157-
check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
146+
fn add_explicit_type_not_applicable_closure_expr() {
147+
check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = || {}; }"#);
158148
}
159149

160150
#[test]
161-
fn add_explicit_type_not_applicable_if_specified_ty_is_tuple() {
162-
check_assist_not_applicable(
163-
add_explicit_type,
164-
r#"fn f() { let a$0: (i32, i32) = (3, 4); }"#,
165-
);
151+
fn add_explicit_type_not_applicable_ty_already_specified() {
152+
cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
153+
check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
166154
}
167155

168156
#[test]
169-
fn add_explicit_type_not_applicable_if_cursor_after_equals() {
157+
fn add_explicit_type_not_applicable_cursor_after_equals_of_let() {
170158
cov_mark::check!(add_explicit_type_not_applicable_if_cursor_after_equals);
171159
check_assist_not_applicable(
172160
add_explicit_type,
173161
r#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
174162
)
175163
}
176164

177-
#[test]
178-
fn add_explicit_type_not_applicable_if_cursor_before_let() {
179-
check_assist_not_applicable(
180-
add_explicit_type,
181-
r#"fn f() $0{let a = match 1 {2 => 3, 3 => 5};}"#,
182-
)
183-
}
184-
185-
#[test]
186-
fn closure_parameters_are_not_added() {
187-
check_assist_not_applicable(
188-
add_explicit_type,
189-
r#"
190-
fn main() {
191-
let multiply_by_two$0 = |i| i * 3;
192-
let six = multiply_by_two(2);
193-
}
194-
"#,
195-
)
196-
}
197-
198165
/// https://github.com/rust-analyzer/rust-analyzer/issues/2922
199166
#[test]
200167
fn regression_issue_2922() {
@@ -276,6 +243,55 @@ fn f() {
276243
fn f() {
277244
let x: *const [i32] = &[3];
278245
}
246+
"#,
247+
);
248+
}
249+
250+
#[test]
251+
fn add_explicit_type_not_applicable_fn_param() {
252+
cov_mark::check!(add_explicit_type_not_applicable_in_fn_param);
253+
check_assist_not_applicable(add_explicit_type, r#"fn f(x$0: ()) {}"#);
254+
}
255+
256+
#[test]
257+
fn add_explicit_type_ascribes_closure_param() {
258+
check_assist(
259+
add_explicit_type,
260+
r#"
261+
fn f() {
262+
|y$0| {
263+
let x: i32 = y;
264+
};
265+
}
266+
"#,
267+
r#"
268+
fn f() {
269+
|y: i32| {
270+
let x: i32 = y;
271+
};
272+
}
273+
"#,
274+
);
275+
}
276+
277+
#[test]
278+
fn add_explicit_type_ascribes_closure_param_already_ascribed() {
279+
check_assist(
280+
add_explicit_type,
281+
r#"
282+
//- minicore: option
283+
fn f() {
284+
|mut y$0: Option<_>| {
285+
y = Some(3);
286+
};
287+
}
288+
"#,
289+
r#"
290+
fn f() {
291+
|mut y: Option<i32>| {
292+
y = Some(3);
293+
};
294+
}
279295
"#,
280296
);
281297
}

crates/ide_assists/src/handlers/invert_if.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ use crate::{
2626
// if y { B } else { A }
2727
// }
2828
// ```
29-
3029
pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
3130
let if_keyword = ctx.find_token_syntax_at_offset(T![if])?;
3231
let expr = ast::IfExpr::cast(if_keyword.parent()?)?;

crates/syntax/src/ast/node_ext.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,45 @@ impl ast::Pat {
6464
WalkEvent::Enter(node) => node,
6565
WalkEvent::Leave(_) => continue,
6666
};
67-
match ast::Pat::cast(node.clone()) {
68-
Some(ast::Pat::ConstBlockPat(_)) => preorder.skip_subtree(),
67+
let kind = node.kind();
68+
match ast::Pat::cast(node) {
69+
Some(pat @ ast::Pat::ConstBlockPat(_)) => {
70+
preorder.skip_subtree();
71+
cb(pat);
72+
}
6973
Some(pat) => {
7074
cb(pat);
7175
}
7276
// skip const args
73-
None if ast::GenericArg::can_cast(node.kind()) => {
77+
None if ast::GenericArg::can_cast(kind) => {
78+
preorder.skip_subtree();
79+
}
80+
None => (),
81+
}
82+
}
83+
}
84+
}
85+
86+
impl ast::Type {
87+
/// Preorder walk all the type's sub types.
88+
pub fn walk(&self, cb: &mut dyn FnMut(ast::Type)) {
89+
let mut preorder = self.syntax().preorder();
90+
while let Some(event) = preorder.next() {
91+
let node = match event {
92+
WalkEvent::Enter(node) => node,
93+
WalkEvent::Leave(_) => continue,
94+
};
95+
let kind = node.kind();
96+
match ast::Type::cast(node) {
97+
Some(ty @ ast::Type::MacroType(_)) => {
98+
preorder.skip_subtree();
99+
cb(ty)
100+
}
101+
Some(ty) => {
102+
cb(ty);
103+
}
104+
// skip const args
105+
None if ast::ConstArg::can_cast(kind) => {
74106
preorder.skip_subtree();
75107
}
76108
None => (),

0 commit comments

Comments
 (0)