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
| {
51 contains_infer_ty
|= matches
!(ty
, ast
::Type
::InferType(_
));
54 if !contains_infer_ty
{
55 cov_mark
::hit
!(add_explicit_type_not_applicable_if_ty_already_specified
);
60 let ty
= match (pat
, expr
) {
61 (ast
::Pat
::IdentPat(_
), Some(expr
)) => ctx
.sema
.type_of_expr(&expr
)?
,
62 (pat
, _
) => ctx
.sema
.type_of_pat(&pat
)?
,
66 // Fully unresolved or unnameable types can't be annotated
67 if (ty
.contains_unknown() && ty
.type_arguments().count() == 0) || ty
.is_closure() {
68 cov_mark
::hit
!(add_explicit_type_not_applicable_if_ty_not_inferred
);
72 let inferred_type
= ty
.display_source_code(ctx
.db(), module
.into(), false).ok()?
;
74 AssistId("add_explicit_type", AssistKind
::RefactorRewrite
),
75 format
!("Insert explicit type `{inferred_type}`"),
77 |builder
| match ascribed_ty
{
78 Some(ascribed_ty
) => {
79 builder
.replace(ascribed_ty
.syntax().text_range(), inferred_type
);
82 builder
.insert(pat_range
.end(), format
!(": {inferred_type}"));
92 use crate::tests
::{check_assist, check_assist_not_applicable, check_assist_target}
;
95 fn add_explicit_type_target() {
96 check_assist_target(add_explicit_type
, r
#"fn f() { let a$0 = 1; }"#, "a");
100 fn add_explicit_type_simple() {
103 r
#"fn f() { let a$0 = 1; }"#,
104 r
#"fn f() { let a: i32 = 1; }"#,
109 fn add_explicit_type_simple_on_infer_ty() {
112 r
#"fn f() { let a$0: _ = 1; }"#,
113 r
#"fn f() { let a: i32 = 1; }"#,
118 fn add_explicit_type_simple_nested_infer_ty() {
124 let a$0: Option<_> = Option::Some(1);
129 let a: Option<i32> = Option::Some(1);
136 fn add_explicit_type_macro_call_expr() {
139 r
"macro_rules! v { () => {0u64} } fn f() { let a$0 = v!(); }",
140 r
"macro_rules! v { () => {0u64} } fn f() { let a: u64 = v!(); }",
145 fn add_explicit_type_not_applicable_for_fully_unresolved() {
146 cov_mark
::check
!(add_explicit_type_not_applicable_if_ty_not_inferred
);
147 check_assist_not_applicable(add_explicit_type
, r
#"fn f() { let a$0 = None; }"#);
151 fn add_explicit_type_applicable_for_partially_unresolved() {
155 struct Vec<T, V> { t: T, v: V }
156 impl<T> Vec<T, Vec<ZZZ, i32>> {
161 fn f() { let a$0 = Vec::new(); }"#,
163 struct Vec<T, V> { t: T, v: V }
164 impl<T> Vec<T, Vec<ZZZ, i32>> {
169 fn f() { let a: Vec<_, Vec<_, i32>> = Vec::new(); }"#,
174 fn add_explicit_type_not_applicable_closure_expr() {
175 check_assist_not_applicable(add_explicit_type
, r
#"fn f() { let a$0 = || {}; }"#);
179 fn add_explicit_type_not_applicable_ty_already_specified() {
180 cov_mark
::check
!(add_explicit_type_not_applicable_if_ty_already_specified
);
181 check_assist_not_applicable(add_explicit_type
, r
#"fn f() { let a$0: i32 = 1; }"#);
185 fn add_explicit_type_not_applicable_cursor_after_equals_of_let() {
186 cov_mark
::check
!(add_explicit_type_not_applicable_if_cursor_after_equals
);
187 check_assist_not_applicable(
189 r
#"fn f() {let a =$0 match 1 {2 => 3, 3 => 5};}"#,
193 /// https://github.com/rust-lang/rust-analyzer/issues/2922
195 fn regression_issue_2922() {
205 let v: [f64; 2] = [0.0; 2];
209 // note: this may break later if we add more consteval. it just needs to be something that our
210 // consteval engine doesn't understand
211 check_assist_not_applicable(
215 let $0l = [0.0; unresolved_function(5)];
222 fn default_generics_should_not_be_added() {
226 struct Test<K, T = u8> { k: K, t: T }
229 let test$0 = Test { t: 23u8, k: 33 };
233 struct Test<K, T = u8> { k: K, t: T }
236 let test: Test<i32> = Test { t: 23u8, k: 33 };
243 fn type_should_be_added_after_pattern() {
244 // LetStmt = Attr* 'let' Pat (':' Type)? '=' initializer:Expr ';'
249 let $0test @ () = ();
254 let test @ (): () = ();
261 fn add_explicit_type_inserts_coercions() {
265 //- minicore: coerce_unsized
267 let $0x: *const [_] = &[3];
272 let x: *const [i32] = &[3];
279 fn add_explicit_type_not_applicable_fn_param() {
280 cov_mark
::check
!(add_explicit_type_not_applicable_in_fn_param
);
281 check_assist_not_applicable(add_explicit_type
, r
#"fn f(x$0: ()) {}"#);
285 fn add_explicit_type_ascribes_closure_param() {
306 fn add_explicit_type_ascribes_closure_param_already_ascribed() {
312 |mut y$0: Option<_>| {
319 |mut y: Option<i32>| {