1 use hir::{db::AstDatabase, HirDisplay, Type, TypeInfo};
3 famous_defs::FamousDefs, source_change::SourceChange,
4 syntax_helpers::node_ext::for_each_tail_expr,
7 ast::{BlockExpr, ExprStmt},
10 use text_edit::TextEdit;
12 use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
14 // Diagnostic: type-mismatch
16 // This diagnostic is triggered when the type of an expression does not match
18 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
19 let mut diag = Diagnostic::new(
22 "expected {}, found {}",
23 d.expected.display(ctx.sema.db),
24 d.actual.display(ctx.sema.db)
26 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
28 .with_fixes(fixes(ctx, d));
29 if diag.fixes.is_none() {
30 diag.experimental = true;
35 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
36 let mut fixes = Vec::new();
38 add_reference(ctx, d, &mut fixes);
39 add_missing_ok_or_some(ctx, d, &mut fixes);
40 remove_semicolon(ctx, d, &mut fixes);
50 ctx: &DiagnosticsContext<'_>,
51 d: &hir::TypeMismatch,
52 acc: &mut Vec<Assist>,
54 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
55 let expr_node = d.expr.value.to_node(&root);
57 let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
59 let (_, mutability) = d.expected.as_reference()?;
60 let actual_with_ref = Type::reference(&d.actual, mutability);
61 if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
65 let ampersands = format!("&{}", mutability.as_keyword_for_ref());
67 let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
69 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
70 acc.push(fix("add_reference_here", "Add reference here", source_change, range));
74 fn add_missing_ok_or_some(
75 ctx: &DiagnosticsContext<'_>,
76 d: &hir::TypeMismatch,
77 acc: &mut Vec<Assist>,
79 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
80 let tail_expr = d.expr.value.to_node(&root);
81 let tail_expr_range = tail_expr.syntax().text_range();
82 let scope = ctx.sema.scope(tail_expr.syntax());
84 let expected_adt = d.expected.as_adt()?;
85 let expected_enum = expected_adt.as_enum()?;
87 let famous_defs = FamousDefs(&ctx.sema, scope.krate());
88 let core_result = famous_defs.core_result_Result();
89 let core_option = famous_defs.core_option_Option();
91 if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
95 let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
97 let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);
99 if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
103 let mut builder = TextEdit::builder();
104 for_each_tail_expr(&tail_expr, &mut |expr| {
105 if ctx.sema.type_of_expr(expr).map(TypeInfo::adjusted).as_ref() != Some(&d.expected) {
106 builder.insert(expr.syntax().text_range().start(), format!("{}(", variant_name));
107 builder.insert(expr.syntax().text_range().end(), ")".to_string());
111 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), builder.finish());
112 let name = format!("Wrap in {}", variant_name);
113 acc.push(fix("wrap_tail_expr", &name, source_change, tail_expr_range));
118 ctx: &DiagnosticsContext<'_>,
119 d: &hir::TypeMismatch,
120 acc: &mut Vec<Assist>,
122 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
123 let expr = d.expr.value.to_node(&root);
124 if !d.actual.is_unit() {
127 let block = BlockExpr::cast(expr.syntax().clone())?;
128 let expr_before_semi =
129 block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
130 let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
131 if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
134 let semicolon_range = expr_before_semi.semicolon_token()?.text_range();
136 let edit = TextEdit::delete(semicolon_range);
138 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
140 acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
146 use crate::tests::{check_diagnostics, check_fix, check_no_fix};
149 fn missing_reference() {
154 //^^^ 💡 error: expected &i32, found i32
156 fn test(arg: &i32) {}
162 fn test_add_reference_to_int() {
168 fn test(arg: &i32) {}
174 fn test(arg: &i32) {}
180 fn test_add_mutable_reference_to_int() {
186 fn test(arg: &mut i32) {}
192 fn test(arg: &mut i32) {}
198 fn test_add_reference_to_array() {
201 //- minicore: coerce_unsized
205 fn test(arg: &[i32]) {}
211 fn test(arg: &[i32]) {}
217 fn test_add_reference_with_autoderef() {
220 //- minicore: coerce_unsized, deref
223 impl core::ops::Deref for Foo {
230 fn test(arg: &Bar) {}
235 impl core::ops::Deref for Foo {
242 fn test(arg: &Bar) {}
248 fn test_add_reference_to_method_call() {
252 Test.call_by_ref($0123);
256 fn call_by_ref(&self, arg: &i32) {}
261 Test.call_by_ref(&123);
265 fn call_by_ref(&self, arg: &i32) {}
272 fn test_add_reference_to_let_stmt() {
276 let test: &i32 = $0123;
281 let test: &i32 = &123;
288 fn test_add_mutable_reference_to_let_stmt() {
292 let test: &mut i32 = $0123;
297 let test: &mut i32 = &mut 123;
304 fn test_wrap_return_type_option() {
307 //- minicore: option, result
308 fn div(x: i32, y: i32) -> Option<i32> {
316 fn div(x: i32, y: i32) -> Option<i32> {
327 fn test_wrap_return_type_option_tails() {
330 //- minicore: option, result
331 fn div(x: i32, y: i32) -> Option<i32> {
342 fn div(x: i32, y: i32) -> Option<i32> {
356 fn test_wrap_return_type() {
359 //- minicore: option, result
360 fn div(x: i32, y: i32) -> Result<i32, ()> {
368 fn div(x: i32, y: i32) -> Result<i32, ()> {
379 fn test_wrap_return_type_handles_generic_functions() {
382 //- minicore: option, result
383 fn div<T>(x: T) -> Result<T, i32> {
391 fn div<T>(x: T) -> Result<T, i32> {
402 fn test_wrap_return_type_handles_type_aliases() {
405 //- minicore: option, result
406 type MyResult<T> = Result<T, ()>;
408 fn div(x: i32, y: i32) -> MyResult<i32> {
416 type MyResult<T> = Result<T, ()>;
418 fn div(x: i32, y: i32) -> MyResult<i32> {
429 fn test_in_const_and_static() {
432 //- minicore: option, result
433 static A: Option<()> = {($0)};
436 static A: Option<()> = {Some(())};
441 //- minicore: option, result
442 const _: Option<()> = {($0)};
445 const _: Option<()> = {Some(())};
451 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
454 //- minicore: option, result
455 fn foo() -> Result<(), i32> { 0$0 }
461 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
464 //- minicore: option, result
465 enum SomeOtherEnum { Ok(i32), Err(String) }
467 fn foo() -> SomeOtherEnum { 0$0 }
473 fn remove_semicolon() {
474 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);