]> git.lizzy.rs Git - rust.git/blob - crates/ide_diagnostics/src/handlers/type_mismatch.rs
Merge #11878
[rust.git] / crates / ide_diagnostics / src / handlers / type_mismatch.rs
1 use hir::{db::AstDatabase, HirDisplay, Type};
2 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
3 use syntax::{
4     ast::{BlockExpr, ExprStmt},
5     AstNode,
6 };
7 use text_edit::TextEdit;
8
9 use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
10
11 // Diagnostic: type-mismatch
12 //
13 // This diagnostic is triggered when the type of an expression does not match
14 // the expected type.
15 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
16     let mut diag = Diagnostic::new(
17         "type-mismatch",
18         format!(
19             "expected {}, found {}",
20             d.expected.display(ctx.sema.db),
21             d.actual.display(ctx.sema.db)
22         ),
23         ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
24     )
25     .with_fixes(fixes(ctx, d));
26     if diag.fixes.is_none() {
27         diag.experimental = true;
28     }
29     diag
30 }
31
32 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
33     let mut fixes = Vec::new();
34
35     add_reference(ctx, d, &mut fixes);
36     add_missing_ok_or_some(ctx, d, &mut fixes);
37     remove_semicolon(ctx, d, &mut fixes);
38
39     if fixes.is_empty() {
40         None
41     } else {
42         Some(fixes)
43     }
44 }
45
46 fn add_reference(
47     ctx: &DiagnosticsContext<'_>,
48     d: &hir::TypeMismatch,
49     acc: &mut Vec<Assist>,
50 ) -> Option<()> {
51     let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
52     let expr_node = d.expr.value.to_node(&root);
53
54     let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
55
56     let (_, mutability) = d.expected.as_reference()?;
57     let actual_with_ref = Type::reference(&d.actual, mutability);
58     if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
59         return None;
60     }
61
62     let ampersands = format!("&{}", mutability.as_keyword_for_ref());
63
64     let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
65     let source_change =
66         SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
67     acc.push(fix("add_reference_here", "Add reference here", source_change, range));
68     Some(())
69 }
70
71 fn add_missing_ok_or_some(
72     ctx: &DiagnosticsContext<'_>,
73     d: &hir::TypeMismatch,
74     acc: &mut Vec<Assist>,
75 ) -> Option<()> {
76     let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
77     let expr = d.expr.value.to_node(&root);
78     let expr_range = expr.syntax().text_range();
79     let scope = ctx.sema.scope(expr.syntax())?;
80
81     let expected_adt = d.expected.as_adt()?;
82     let expected_enum = expected_adt.as_enum()?;
83
84     let famous_defs = FamousDefs(&ctx.sema, scope.krate());
85     let core_result = famous_defs.core_result_Result();
86     let core_option = famous_defs.core_option_Option();
87
88     if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
89         return None;
90     }
91
92     let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
93
94     let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);
95
96     if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
97         return None;
98     }
99
100     let mut builder = TextEdit::builder();
101     builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
102     builder.insert(expr.syntax().text_range().end(), ")".to_string());
103     let source_change =
104         SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
105     let name = format!("Wrap in {}", variant_name);
106     acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
107     Some(())
108 }
109
110 fn remove_semicolon(
111     ctx: &DiagnosticsContext<'_>,
112     d: &hir::TypeMismatch,
113     acc: &mut Vec<Assist>,
114 ) -> Option<()> {
115     let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
116     let expr = d.expr.value.to_node(&root);
117     if !d.actual.is_unit() {
118         return None;
119     }
120     let block = BlockExpr::cast(expr.syntax().clone())?;
121     let expr_before_semi =
122         block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
123     let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
124     if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
125         return None;
126     }
127     let semicolon_range = expr_before_semi.semicolon_token()?.text_range();
128
129     let edit = TextEdit::delete(semicolon_range);
130     let source_change =
131         SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
132
133     acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
134     Some(())
135 }
136
137 #[cfg(test)]
138 mod tests {
139     use crate::tests::{check_diagnostics, check_fix, check_no_fix};
140
141     #[test]
142     fn missing_reference() {
143         check_diagnostics(
144             r#"
145 fn main() {
146     test(123);
147        //^^^ ðŸ’¡ error: expected &i32, found i32
148 }
149 fn test(arg: &i32) {}
150 "#,
151         );
152     }
153
154     #[test]
155     fn test_add_reference_to_int() {
156         check_fix(
157             r#"
158 fn main() {
159     test(123$0);
160 }
161 fn test(arg: &i32) {}
162             "#,
163             r#"
164 fn main() {
165     test(&123);
166 }
167 fn test(arg: &i32) {}
168             "#,
169         );
170     }
171
172     #[test]
173     fn test_add_mutable_reference_to_int() {
174         check_fix(
175             r#"
176 fn main() {
177     test($0123);
178 }
179 fn test(arg: &mut i32) {}
180             "#,
181             r#"
182 fn main() {
183     test(&mut 123);
184 }
185 fn test(arg: &mut i32) {}
186             "#,
187         );
188     }
189
190     #[test]
191     fn test_add_reference_to_array() {
192         check_fix(
193             r#"
194 //- minicore: coerce_unsized
195 fn main() {
196     test($0[1, 2, 3]);
197 }
198 fn test(arg: &[i32]) {}
199             "#,
200             r#"
201 fn main() {
202     test(&[1, 2, 3]);
203 }
204 fn test(arg: &[i32]) {}
205             "#,
206         );
207     }
208
209     #[test]
210     fn test_add_reference_with_autoderef() {
211         check_fix(
212             r#"
213 //- minicore: coerce_unsized, deref
214 struct Foo;
215 struct Bar;
216 impl core::ops::Deref for Foo {
217     type Target = Bar;
218 }
219
220 fn main() {
221     test($0Foo);
222 }
223 fn test(arg: &Bar) {}
224             "#,
225             r#"
226 struct Foo;
227 struct Bar;
228 impl core::ops::Deref for Foo {
229     type Target = Bar;
230 }
231
232 fn main() {
233     test(&Foo);
234 }
235 fn test(arg: &Bar) {}
236             "#,
237         );
238     }
239
240     #[test]
241     fn test_add_reference_to_method_call() {
242         check_fix(
243             r#"
244 fn main() {
245     Test.call_by_ref($0123);
246 }
247 struct Test;
248 impl Test {
249     fn call_by_ref(&self, arg: &i32) {}
250 }
251             "#,
252             r#"
253 fn main() {
254     Test.call_by_ref(&123);
255 }
256 struct Test;
257 impl Test {
258     fn call_by_ref(&self, arg: &i32) {}
259 }
260             "#,
261         );
262     }
263
264     #[test]
265     fn test_add_reference_to_let_stmt() {
266         check_fix(
267             r#"
268 fn main() {
269     let test: &i32 = $0123;
270 }
271             "#,
272             r#"
273 fn main() {
274     let test: &i32 = &123;
275 }
276             "#,
277         );
278     }
279
280     #[test]
281     fn test_add_mutable_reference_to_let_stmt() {
282         check_fix(
283             r#"
284 fn main() {
285     let test: &mut i32 = $0123;
286 }
287             "#,
288             r#"
289 fn main() {
290     let test: &mut i32 = &mut 123;
291 }
292             "#,
293         );
294     }
295
296     #[test]
297     fn test_wrap_return_type_option() {
298         check_fix(
299             r#"
300 //- minicore: option, result
301 fn div(x: i32, y: i32) -> Option<i32> {
302     if y == 0 {
303         return None;
304     }
305     x / y$0
306 }
307 "#,
308             r#"
309 fn div(x: i32, y: i32) -> Option<i32> {
310     if y == 0 {
311         return None;
312     }
313     Some(x / y)
314 }
315 "#,
316         );
317     }
318
319     #[test]
320     fn test_wrap_return_type_option_tails() {
321         check_fix(
322             r#"
323 //- minicore: option, result
324 fn div(x: i32, y: i32) -> Option<i32> {
325     if y == 0 {
326         Some(0)
327     } else if true {
328         100$0
329     } else {
330         None
331     }
332 }
333 "#,
334             r#"
335 fn div(x: i32, y: i32) -> Option<i32> {
336     if y == 0 {
337         Some(0)
338     } else if true {
339         Some(100)
340     } else {
341         None
342     }
343 }
344 "#,
345         );
346     }
347
348     #[test]
349     fn test_wrap_return_type() {
350         check_fix(
351             r#"
352 //- minicore: option, result
353 fn div(x: i32, y: i32) -> Result<i32, ()> {
354     if y == 0 {
355         return Err(());
356     }
357     x / y$0
358 }
359 "#,
360             r#"
361 fn div(x: i32, y: i32) -> Result<i32, ()> {
362     if y == 0 {
363         return Err(());
364     }
365     Ok(x / y)
366 }
367 "#,
368         );
369     }
370
371     #[test]
372     fn test_wrap_return_type_handles_generic_functions() {
373         check_fix(
374             r#"
375 //- minicore: option, result
376 fn div<T>(x: T) -> Result<T, i32> {
377     if x == 0 {
378         return Err(7);
379     }
380     $0x
381 }
382 "#,
383             r#"
384 fn div<T>(x: T) -> Result<T, i32> {
385     if x == 0 {
386         return Err(7);
387     }
388     Ok(x)
389 }
390 "#,
391         );
392     }
393
394     #[test]
395     fn test_wrap_return_type_handles_type_aliases() {
396         check_fix(
397             r#"
398 //- minicore: option, result
399 type MyResult<T> = Result<T, ()>;
400
401 fn div(x: i32, y: i32) -> MyResult<i32> {
402     if y == 0 {
403         return Err(());
404     }
405     x $0/ y
406 }
407 "#,
408             r#"
409 type MyResult<T> = Result<T, ()>;
410
411 fn div(x: i32, y: i32) -> MyResult<i32> {
412     if y == 0 {
413         return Err(());
414     }
415     Ok(x / y)
416 }
417 "#,
418         );
419     }
420
421     #[test]
422     fn test_in_const_and_static() {
423         check_fix(
424             r#"
425 //- minicore: option, result
426 static A: Option<()> = {($0)};
427             "#,
428             r#"
429 static A: Option<()> = {Some(())};
430             "#,
431         );
432         check_fix(
433             r#"
434 //- minicore: option, result
435 const _: Option<()> = {($0)};
436             "#,
437             r#"
438 const _: Option<()> = {Some(())};
439             "#,
440         );
441     }
442
443     #[test]
444     fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
445         check_no_fix(
446             r#"
447 //- minicore: option, result
448 fn foo() -> Result<(), i32> { 0$0 }
449 "#,
450         );
451     }
452
453     #[test]
454     fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
455         check_no_fix(
456             r#"
457 //- minicore: option, result
458 enum SomeOtherEnum { Ok(i32), Err(String) }
459
460 fn foo() -> SomeOtherEnum { 0$0 }
461 "#,
462         );
463     }
464
465     #[test]
466     fn remove_semicolon() {
467         check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
468     }
469 }