2 use ide_db::syntax_helpers::node_ext::walk_ty;
3 use syntax::ast::{self, AstNode, LetStmt, Param};
5 use crate::{AssistContext, AssistId, AssistKind, Assists};
7 // Assist: add_explicit_type
9 // Specify type for a let binding.
22 pub(crate) fn add_explicit_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
23 let (ascribed_ty, expr, pat) = if let Some(let_stmt) = ctx.find_node_at_offset::<LetStmt>() {
24 let cursor_in_range = {
25 let eq_range = let_stmt.eq_token()?.text_range();
26 ctx.offset() < eq_range.start()
29 cov_mark::hit!(add_explicit_type_not_applicable_if_cursor_after_equals);
33 (let_stmt.ty(), let_stmt.initializer(), let_stmt.pat()?)
34 } else if let Some(param) = ctx.find_node_at_offset::<Param>() {
35 if param.syntax().ancestors().nth(2).and_then(ast::ClosureExpr::cast).is_none() {
36 cov_mark::hit!(add_explicit_type_not_applicable_in_fn_param);
39 (param.ty(), None, param.pat()?)
44 let module = ctx.sema.scope(pat.syntax())?.module();
45 let pat_range = pat.syntax().text_range();
47 // Don't enable the assist if there is a type ascription without any placeholders
48 if let Some(ty) = &ascribed_ty {
49 let mut contains_infer_ty = false;
50 walk_ty(ty, &mut |ty| contains_infer_ty |= matches!(ty, ast::Type::InferType(_)));
51 if !contains_infer_ty {
52 cov_mark::hit!(add_explicit_type_not_applicable_if_ty_already_specified);
57 let ty = match (pat, expr) {
58 (ast::Pat::IdentPat(_), Some(expr)) => ctx.sema.type_of_expr(&expr)?,
59 (pat, _) => ctx.sema.type_of_pat(&pat)?,
63 // Fully unresolved or unnameable types can't be annotated
64 if (ty.contains_unknown() && ty.type_arguments().count() == 0) || ty.is_closure() {
65 cov_mark::hit!(add_explicit_type_not_applicable_if_ty_not_inferred);
69 let inferred_type = ty.display_source_code(ctx.db(), module.into()).ok()?;
71 AssistId("add_explicit_type", AssistKind::RefactorRewrite),
72 format!("Insert explicit type `{}`", inferred_type),
74 |builder| match ascribed_ty {
75 Some(ascribed_ty) => {
76 builder.replace(ascribed_ty.syntax().text_range(), inferred_type);
79 builder.insert(pat_range.end(), format!(": {}", inferred_type));
89 use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
92 fn add_explicit_type_target() {
93 check_assist_target(add_explicit_type, r#"fn f() { let a$0 = 1; }"#, "a");
97 fn add_explicit_type_simple() {
100 r#"fn f() { let a$0 = 1; }"#,
101 r#"fn f() { let a: i32 = 1; }"#,
106 fn add_explicit_type_simple_on_infer_ty() {
109 r#"fn f() { let a$0: _ = 1; }"#,
110 r#"fn f() { let a: i32 = 1; }"#,
115 fn add_explicit_type_simple_nested_infer_ty() {
121 let a$0: Option<_> = Option::Some(1);
126 let a: Option<i32> = Option::Some(1);
133 fn add_explicit_type_macro_call_expr() {
136 r"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
137 r"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
142 fn add_explicit_type_not_applicable_for_fully_unresolved() {
143 cov_mark::check!(add_explicit_type_not_applicable_if_ty_not_inferred);
144 check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = None; }"#);
148 fn add_explicit_type_applicable_for_partially_unresolved() {
152 struct Vec<T, V> { t: T, v: V }
153 impl<T> Vec<T, Vec<ZZZ, i32>> {
158 fn f() { let a$0 = Vec::new(); }"#,
160 struct Vec<T, V> { t: T, v: V }
161 impl<T> Vec<T, Vec<ZZZ, i32>> {
166 fn f() { let a: Vec<_, Vec<_, i32>> = Vec::new(); }"#,
171 fn add_explicit_type_not_applicable_closure_expr() {
172 check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0 = || {}; }"#);
176 fn add_explicit_type_not_applicable_ty_already_specified() {
177 cov_mark::check!(add_explicit_type_not_applicable_if_ty_already_specified);
178 check_assist_not_applicable(add_explicit_type, r#"fn f() { let a$0: i32 = 1; }"#);
182 fn add_explicit_type_not_applicable_cursor_after_equals_of_let() {
183 cov_mark::check!(add_explicit_type_not_applicable_if_cursor_after_equals);
184 check_assist_not_applicable(
186 r#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
190 /// https://github.com/rust-lang/rust-analyzer/issues/2922
192 fn regression_issue_2922() {
202 let v: [f64; 2] = [0.0; 2];
206 // note: this may break later if we add more consteval. it just needs to be something that our
207 // consteval engine doesn't understand
208 check_assist_not_applicable(
214 let $0l = [0.0; Some(2).unwrap()];
221 fn default_generics_should_not_be_added() {
225 struct Test<K, T = u8> { k: K, t: T }
228 let test$0 = Test { t: 23u8, k: 33 };
232 struct Test<K, T = u8> { k: K, t: T }
235 let test: Test<i32> = Test { t: 23u8, k: 33 };
242 fn type_should_be_added_after_pattern() {
243 // LetStmt = Attr* 'let' Pat (':' Type)? '=' initializer:Expr ';'
248 let $0test @ () = ();
253 let test @ (): () = ();
260 fn add_explicit_type_inserts_coercions() {
264 //- minicore: coerce_unsized
266 let $0x: *const [_] = &[3];
271 let x: *const [i32] = &[3];
278 fn add_explicit_type_not_applicable_fn_param() {
279 cov_mark::check!(add_explicit_type_not_applicable_in_fn_param);
280 check_assist_not_applicable(add_explicit_type, r#"fn f(x$0: ()) {}"#);
284 fn add_explicit_type_ascribes_closure_param() {
305 fn add_explicit_type_ascribes_closure_param_already_ascribed() {
311 |mut y$0: Option<_>| {
318 |mut y: Option<i32>| {