1 use hir::{db::AstDatabase, HirDisplay, Type};
2 use ide_db::{famous_defs::FamousDefs, source_change::SourceChange};
4 ast::{BlockExpr, ExprStmt},
7 use text_edit::TextEdit;
9 use crate::{fix, Assist, Diagnostic, DiagnosticsContext};
11 // Diagnostic: type-mismatch
13 // This diagnostic is triggered when the type of an expression does not match
15 pub(crate) fn type_mismatch(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Diagnostic {
16 let mut diag = Diagnostic::new(
19 "expected {}, found {}",
20 d.expected.display(ctx.sema.db),
21 d.actual.display(ctx.sema.db)
23 ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range,
25 .with_fixes(fixes(ctx, d));
26 if diag.fixes.is_none() {
27 diag.experimental = true;
32 fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypeMismatch) -> Option<Vec<Assist>> {
33 let mut fixes = Vec::new();
35 add_reference(ctx, d, &mut fixes);
36 add_missing_ok_or_some(ctx, d, &mut fixes);
37 remove_semicolon(ctx, d, &mut fixes);
47 ctx: &DiagnosticsContext<'_>,
48 d: &hir::TypeMismatch,
49 acc: &mut Vec<Assist>,
51 let root = ctx.sema.db.parse_or_expand(d.expr.file_id)?;
52 let expr_node = d.expr.value.to_node(&root);
54 let range = ctx.sema.diagnostics_display_range(d.expr.clone().map(|it| it.into())).range;
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) {
62 let ampersands = format!("&{}", mutability.as_keyword_for_ref());
64 let edit = TextEdit::insert(expr_node.syntax().text_range().start(), ampersands);
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));
71 fn add_missing_ok_or_some(
72 ctx: &DiagnosticsContext<'_>,
73 d: &hir::TypeMismatch,
74 acc: &mut Vec<Assist>,
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())?;
81 let expected_adt = d.expected.as_adt()?;
82 let expected_enum = expected_adt.as_enum()?;
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();
88 if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
92 let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
94 let wrapped_actual_ty = expected_adt.ty_with_args(ctx.sema.db, &[d.actual.clone()]);
96 if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
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());
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));
111 ctx: &DiagnosticsContext<'_>,
112 d: &hir::TypeMismatch,
113 acc: &mut Vec<Assist>,
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() {
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) {
127 let semicolon_range = expr_before_semi.semicolon_token()?.text_range();
129 let edit = TextEdit::delete(semicolon_range);
131 SourceChange::from_text_edit(d.expr.file_id.original_file(ctx.sema.db), edit);
133 acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
139 use crate::tests::{check_diagnostics, check_fix, check_no_fix};
142 fn missing_reference() {
147 //^^^ 💡 error: expected &i32, found i32
149 fn test(arg: &i32) {}
155 fn test_add_reference_to_int() {
161 fn test(arg: &i32) {}
167 fn test(arg: &i32) {}
173 fn test_add_mutable_reference_to_int() {
179 fn test(arg: &mut i32) {}
185 fn test(arg: &mut i32) {}
191 fn test_add_reference_to_array() {
194 //- minicore: coerce_unsized
198 fn test(arg: &[i32]) {}
204 fn test(arg: &[i32]) {}
210 fn test_add_reference_with_autoderef() {
213 //- minicore: coerce_unsized, deref
216 impl core::ops::Deref for Foo {
223 fn test(arg: &Bar) {}
228 impl core::ops::Deref for Foo {
235 fn test(arg: &Bar) {}
241 fn test_add_reference_to_method_call() {
245 Test.call_by_ref($0123);
249 fn call_by_ref(&self, arg: &i32) {}
254 Test.call_by_ref(&123);
258 fn call_by_ref(&self, arg: &i32) {}
265 fn test_add_reference_to_let_stmt() {
269 let test: &i32 = $0123;
274 let test: &i32 = &123;
281 fn test_add_mutable_reference_to_let_stmt() {
285 let test: &mut i32 = $0123;
290 let test: &mut i32 = &mut 123;
297 fn test_wrap_return_type_option() {
300 //- minicore: option, result
301 fn div(x: i32, y: i32) -> Option<i32> {
309 fn div(x: i32, y: i32) -> Option<i32> {
320 fn test_wrap_return_type_option_tails() {
323 //- minicore: option, result
324 fn div(x: i32, y: i32) -> Option<i32> {
335 fn div(x: i32, y: i32) -> Option<i32> {
349 fn test_wrap_return_type() {
352 //- minicore: option, result
353 fn div(x: i32, y: i32) -> Result<i32, ()> {
361 fn div(x: i32, y: i32) -> Result<i32, ()> {
372 fn test_wrap_return_type_handles_generic_functions() {
375 //- minicore: option, result
376 fn div<T>(x: T) -> Result<T, i32> {
384 fn div<T>(x: T) -> Result<T, i32> {
395 fn test_wrap_return_type_handles_type_aliases() {
398 //- minicore: option, result
399 type MyResult<T> = Result<T, ()>;
401 fn div(x: i32, y: i32) -> MyResult<i32> {
409 type MyResult<T> = Result<T, ()>;
411 fn div(x: i32, y: i32) -> MyResult<i32> {
422 fn test_in_const_and_static() {
425 //- minicore: option, result
426 static A: Option<()> = {($0)};
429 static A: Option<()> = {Some(())};
434 //- minicore: option, result
435 const _: Option<()> = {($0)};
438 const _: Option<()> = {Some(())};
444 fn test_wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
447 //- minicore: option, result
448 fn foo() -> Result<(), i32> { 0$0 }
454 fn test_wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
457 //- minicore: option, result
458 enum SomeOtherEnum { Ok(i32), Err(String) }
460 fn foo() -> SomeOtherEnum { 0$0 }
466 fn remove_semicolon() {
467 check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);