Skip to main content

ide_diagnostics/handlers/
type_mismatch.rs

1use either::Either;
2use hir::{CallableKind, ClosureStyle, HirDisplay, InFile, db::ExpandDatabase};
3use ide_db::{
4    famous_defs::FamousDefs,
5    source_change::{SourceChange, SourceChangeBuilder},
6    text_edit::TextEdit,
7};
8use syntax::{
9    AstNode, AstPtr, TextSize,
10    ast::{
11        self, BlockExpr, Expr, ExprStmt, HasArgList,
12        edit::{AstNodeEdit, IndentLevel},
13    },
14};
15
16use crate::{Assist, Diagnostic, DiagnosticCode, DiagnosticsContext, adjusted_display_range, fix};
17
18// Diagnostic: type-mismatch
19//
20// This diagnostic is triggered when the type of an expression or pattern does not match
21// the expected type.
22pub(crate) fn type_mismatch(
23    ctx: &DiagnosticsContext<'_, '_>,
24    d: &hir::TypeMismatch<'_>,
25) -> Option<Diagnostic> {
26    if d.expected.is_unknown() || d.actual.is_unknown() {
27        return None;
28    }
29
30    let display_range = adjusted_display_range(ctx, d.expr_or_pat, &|node| {
31        let Either::Left(expr) = node else { return None };
32        let salient_token_range = match expr {
33            ast::Expr::IfExpr(it) => it.if_token()?.text_range(),
34            ast::Expr::LoopExpr(it) => it.loop_token()?.text_range(),
35            ast::Expr::ForExpr(it) => it.for_token()?.text_range(),
36            ast::Expr::WhileExpr(it) => it.while_token()?.text_range(),
37            ast::Expr::BlockExpr(it) => it.stmt_list()?.r_curly_token()?.text_range(),
38            ast::Expr::MatchExpr(it) => it.match_token()?.text_range(),
39            ast::Expr::MethodCallExpr(it) => it.name_ref()?.ident_token()?.text_range(),
40            ast::Expr::FieldExpr(it) => it.name_ref()?.ident_token()?.text_range(),
41            ast::Expr::AwaitExpr(it) => it.await_token()?.text_range(),
42            _ => return None,
43        };
44
45        cov_mark::hit!(type_mismatch_range_adjustment);
46        Some(salient_token_range)
47    });
48    Some(
49        Diagnostic::new(
50            DiagnosticCode::RustcHardError("E0308"),
51            format!(
52                "expected {}, found {}",
53                d.expected
54                    .display(ctx.sema.db, ctx.display_target)
55                    .with_closure_style(ClosureStyle::ClosureWithId),
56                d.actual
57                    .display(ctx.sema.db, ctx.display_target)
58                    .with_closure_style(ClosureStyle::ClosureWithId),
59            ),
60            display_range,
61        )
62        .stable()
63        .with_fixes(fixes(ctx, d)),
64    )
65}
66
67fn fixes(ctx: &DiagnosticsContext<'_, '_>, d: &hir::TypeMismatch<'_>) -> Option<Vec<Assist>> {
68    let mut fixes = Vec::new();
69
70    if let Some(expr_ptr) = d.expr_or_pat.value.cast::<ast::Expr>() {
71        let expr_ptr = &InFile { file_id: d.expr_or_pat.file_id, value: expr_ptr };
72        add_reference(ctx, d, expr_ptr, &mut fixes);
73        add_missing_ok_or_some(ctx, d, expr_ptr, &mut fixes);
74        remove_unnecessary_wrapper(ctx, d, expr_ptr, &mut fixes);
75        remove_semicolon(ctx, d, expr_ptr, &mut fixes);
76        str_ref_to_owned(ctx, d, expr_ptr, &mut fixes);
77    }
78
79    if fixes.is_empty() { None } else { Some(fixes) }
80}
81
82fn add_reference(
83    ctx: &DiagnosticsContext<'_, '_>,
84    d: &hir::TypeMismatch<'_>,
85    expr_ptr: &InFile<AstPtr<ast::Expr>>,
86    acc: &mut Vec<Assist>,
87) -> Option<()> {
88    let range = ctx.sema.diagnostics_display_range((*expr_ptr).map(|it| it.into()));
89
90    let (_, mutability) = d.expected.as_reference()?;
91    let actual_with_ref = d.actual.add_reference(ctx.db(), mutability);
92    if !actual_with_ref.could_coerce_to(ctx.sema.db, &d.expected) {
93        return None;
94    }
95
96    let ampersands = format!("&{}", mutability.as_keyword_for_ref());
97
98    let edit = TextEdit::insert(range.range.start(), ampersands);
99    let source_change = SourceChange::from_text_edit(range.file_id, edit);
100    acc.push(fix("add_reference_here", "Add reference here", source_change, range.range));
101    Some(())
102}
103
104fn add_missing_ok_or_some(
105    ctx: &DiagnosticsContext<'_, '_>,
106    d: &hir::TypeMismatch<'_>,
107    expr_ptr: &InFile<AstPtr<ast::Expr>>,
108    acc: &mut Vec<Assist>,
109) -> Option<()> {
110    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
111    let expr = expr_ptr.value.to_node(&root);
112    let hir::FileRange { file_id, range: expr_range } =
113        ctx.sema.original_range_opt(expr.syntax())?;
114    let scope = ctx.sema.scope(expr.syntax())?;
115
116    let expected_adt = d.expected.as_adt()?;
117    let expected_enum = expected_adt.as_enum()?;
118
119    let famous_defs = FamousDefs(&ctx.sema, scope.krate());
120    let core_result = famous_defs.core_result_Result();
121    let core_option = famous_defs.core_option_Option();
122
123    if Some(expected_enum) != core_result && Some(expected_enum) != core_option {
124        return None;
125    }
126
127    let variant_name = if Some(expected_enum) == core_result { "Ok" } else { "Some" };
128
129    let wrapped_actual_ty =
130        expected_adt.ty_with_args(ctx.sema.db, std::iter::once(d.actual.clone()));
131
132    if !d.expected.could_unify_with(ctx.sema.db, &wrapped_actual_ty) {
133        return None;
134    }
135
136    let file_id = file_id.file_id(ctx.sema.db);
137
138    if d.actual.is_unit() {
139        if let Expr::BlockExpr(block) = &expr {
140            if block.tail_expr().is_none() {
141                // Fix for forms like `fn foo() -> Result<(), String> {}`
142                let mut builder = TextEdit::builder();
143                let block_indent = block.indent_level();
144
145                if block.statements().count() == 0 {
146                    // Empty block
147                    let indent = block_indent + 1;
148                    builder.insert(
149                        expr_range.start() + TextSize::from(1),
150                        format!("\n{indent}{variant_name}(())\n{block_indent}"),
151                    );
152                } else {
153                    let indent = IndentLevel::from(1);
154                    builder.insert(
155                        expr_range.end() - TextSize::from(1),
156                        format!("{indent}{variant_name}(())\n{block_indent}"),
157                    );
158                }
159
160                let source_change = SourceChange::from_text_edit(file_id, builder.finish());
161                let name = format!("Insert {variant_name}(()) as the tail of this block");
162                acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
163            }
164            return Some(());
165        } else if let Expr::ReturnExpr(ret_expr) = &expr {
166            // Fix for forms like `fn foo() -> Result<(), String> { return; }`
167            if ret_expr.expr().is_none() {
168                let mut builder = TextEdit::builder();
169                builder.insert(expr_range.end(), format!(" {variant_name}(())"));
170                let source_change = SourceChange::from_text_edit(file_id, builder.finish());
171                let name = format!("Insert {variant_name}(()) as the return value");
172                acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
173            }
174            return Some(());
175        } else if expr.is_block_like()
176            && expr.syntax().parent().and_then(ast::StmtList::cast).is_some()
177        {
178            // Fix for forms like `fn foo() -> Result<(), String> { for _ in 0..8 {} }`
179            let mut builder = TextEdit::builder();
180            let indent = expr.indent_level();
181            builder.insert(expr_range.end(), format!("\n{indent}{variant_name}(())"));
182
183            let source_change = SourceChange::from_text_edit(file_id, builder.finish());
184            let name = format!("Insert {variant_name}(()) as the tail of this block");
185            acc.push(fix("insert_wrapped_unit", &name, source_change, expr_range));
186            return Some(());
187        }
188    }
189
190    let mut builder = TextEdit::builder();
191    builder.insert(expr_range.start(), format!("{variant_name}("));
192    builder.insert(expr_range.end(), ")".to_owned());
193    let source_change = SourceChange::from_text_edit(file_id, builder.finish());
194    let name = format!("Wrap in {variant_name}");
195    acc.push(fix("wrap_in_constructor", &name, source_change, expr_range));
196    Some(())
197}
198
199fn remove_unnecessary_wrapper(
200    ctx: &DiagnosticsContext<'_, '_>,
201    d: &hir::TypeMismatch<'_>,
202    expr_ptr: &InFile<AstPtr<ast::Expr>>,
203    acc: &mut Vec<Assist>,
204) -> Option<()> {
205    let db = ctx.sema.db;
206    let root = db.parse_or_expand(expr_ptr.file_id);
207    let expr = expr_ptr.value.to_node(&root);
208    // FIXME: support inside MacroCall?
209    let expr = ctx.sema.original_ast_node(expr)?;
210
211    let Expr::CallExpr(call_expr) = expr else {
212        return None;
213    };
214
215    let callable = ctx.sema.resolve_expr_as_callable(&call_expr.expr()?)?;
216    let CallableKind::TupleEnumVariant(variant) = callable.kind() else {
217        return None;
218    };
219
220    let actual_enum = d.actual.as_adt()?.as_enum()?;
221    let famous_defs = FamousDefs(&ctx.sema, ctx.sema.scope(call_expr.syntax())?.krate());
222    let core_option = famous_defs.core_option_Option();
223    let core_result = famous_defs.core_result_Result();
224    if Some(actual_enum) != core_option && Some(actual_enum) != core_result {
225        return None;
226    }
227
228    let inner_type = variant.fields(db).first()?.ty_with_args(db, d.actual.type_arguments());
229    if !d.expected.could_unify_with(db, &inner_type) {
230        return None;
231    }
232
233    let inner_arg = call_expr.arg_list()?.args().next()?;
234
235    let file_id = expr_ptr.file_id.original_file(db);
236    let mut builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db));
237    let editor;
238    match inner_arg {
239        // We're returning `()`
240        Expr::TupleExpr(tup) if tup.fields().next().is_none() => {
241            let parent = call_expr
242                .syntax()
243                .parent()
244                .and_then(Either::<ast::ReturnExpr, ast::StmtList>::cast)?;
245
246            editor = builder.make_editor(parent.syntax());
247            let make = editor.make();
248
249            match parent {
250                Either::Left(ret_expr) => {
251                    editor.replace(ret_expr.syntax(), make.expr_return(None).syntax());
252                }
253                Either::Right(stmt_list) => {
254                    let new_block = if stmt_list.statements().next().is_none() {
255                        make.expr_empty_block()
256                    } else {
257                        make.block_expr(stmt_list.statements(), None)
258                    };
259
260                    editor.replace(stmt_list.syntax().parent()?, new_block.syntax());
261                }
262            }
263        }
264        _ => {
265            editor = builder.make_editor(call_expr.syntax());
266            editor.replace(call_expr.syntax(), inner_arg.syntax());
267        }
268    }
269
270    builder.add_file_edits(file_id.file_id(ctx.sema.db), editor);
271    let name = format!("Remove unnecessary {}() wrapper", variant.name(db).as_str());
272    acc.push(fix(
273        "remove_unnecessary_wrapper",
274        &name,
275        builder.finish(),
276        call_expr.syntax().text_range(),
277    ));
278    Some(())
279}
280
281fn remove_semicolon(
282    ctx: &DiagnosticsContext<'_, '_>,
283    d: &hir::TypeMismatch<'_>,
284    expr_ptr: &InFile<AstPtr<ast::Expr>>,
285    acc: &mut Vec<Assist>,
286) -> Option<()> {
287    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
288    let expr = expr_ptr.value.to_node(&root);
289    if !d.actual.is_unit() {
290        return None;
291    }
292    let block = BlockExpr::cast(expr.syntax().clone())?;
293    // FIXME: support inside MacroCall?
294    let expr_before_semi =
295        block.statements().last().and_then(|s| ExprStmt::cast(s.syntax().clone()))?;
296    let type_before_semi = ctx.sema.type_of_expr(&expr_before_semi.expr()?)?.original();
297    if !type_before_semi.could_coerce_to(ctx.sema.db, &d.expected) {
298        return None;
299    }
300    let semicolon_range = expr_before_semi.semicolon_token()?.text_range();
301
302    let edit = TextEdit::delete(semicolon_range);
303    let source_change = SourceChange::from_text_edit(
304        expr_ptr.file_id.original_file(ctx.sema.db).file_id(ctx.sema.db),
305        edit,
306    );
307
308    acc.push(fix("remove_semicolon", "Remove this semicolon", source_change, semicolon_range));
309    Some(())
310}
311
312fn str_ref_to_owned(
313    ctx: &DiagnosticsContext<'_, '_>,
314    d: &hir::TypeMismatch<'_>,
315    expr_ptr: &InFile<AstPtr<ast::Expr>>,
316    acc: &mut Vec<Assist>,
317) -> Option<()> {
318    let expected = d.expected.display(ctx.sema.db, ctx.display_target);
319    // FIXME do this properly
320    let is_applicable = d.actual.strip_reference().is_str() && expected.to_string() == "String";
321    if !is_applicable {
322        return None;
323    }
324
325    let root = ctx.sema.db.parse_or_expand(expr_ptr.file_id);
326    let expr = expr_ptr.value.to_node(&root);
327    let hir::FileRange { file_id, range } = ctx.sema.original_range_opt(expr.syntax())?;
328
329    let to_owned = ".to_owned()".to_owned();
330
331    let edit = TextEdit::insert(range.end(), to_owned);
332    let source_change = SourceChange::from_text_edit(file_id.file_id(ctx.sema.db), edit);
333    acc.push(fix("str_ref_to_owned", "Add .to_owned() here", source_change, range));
334
335    Some(())
336}
337
338#[cfg(test)]
339mod tests {
340    use crate::tests::{
341        check_diagnostics, check_diagnostics_with_disabled, check_fix, check_fix_with_disabled,
342        check_has_fix, check_no_fix,
343    };
344
345    #[test]
346    fn missing_reference() {
347        check_diagnostics(
348            r#"
349fn main() {
350    test(123);
351       //^^^ 💡 error: expected &i32, found i32
352}
353fn test(_arg: &i32) {}
354"#,
355        );
356    }
357
358    #[test]
359    fn add_reference_to_int() {
360        check_fix(
361            r#"
362fn main() {
363    test(123$0);
364}
365fn test(_arg: &i32) {}
366            "#,
367            r#"
368fn main() {
369    test(&123);
370}
371fn test(_arg: &i32) {}
372            "#,
373        );
374    }
375
376    #[test]
377    fn add_mutable_reference_to_int() {
378        check_fix(
379            r#"
380fn main() {
381    test($0123);
382}
383fn test(_arg: &mut i32) {}
384            "#,
385            r#"
386fn main() {
387    test(&mut 123);
388}
389fn test(_arg: &mut i32) {}
390            "#,
391        );
392    }
393
394    #[test]
395    fn add_reference_to_array() {
396        check_fix(
397            r#"
398//- minicore: coerce_unsized
399fn main() {
400    test($0[1, 2, 3]);
401}
402fn test(_arg: &[i32]) {}
403            "#,
404            r#"
405fn main() {
406    test(&[1, 2, 3]);
407}
408fn test(_arg: &[i32]) {}
409            "#,
410        );
411    }
412
413    #[test]
414    fn add_reference_with_autoderef() {
415        check_fix(
416            r#"
417//- minicore: coerce_unsized, deref
418struct Foo;
419struct Bar;
420impl core::ops::Deref for Foo {
421    type Target = Bar;
422    fn deref(&self) -> &Self::Target { loop {} }
423}
424
425fn main() {
426    test($0Foo);
427}
428fn test(_arg: &Bar) {}
429            "#,
430            r#"
431struct Foo;
432struct Bar;
433impl core::ops::Deref for Foo {
434    type Target = Bar;
435    fn deref(&self) -> &Self::Target { loop {} }
436}
437
438fn main() {
439    test(&Foo);
440}
441fn test(_arg: &Bar) {}
442            "#,
443        );
444    }
445
446    #[test]
447    fn add_reference_to_method_call() {
448        check_fix(
449            r#"
450fn main() {
451    Test.call_by_ref($0123);
452}
453struct Test;
454impl Test {
455    fn call_by_ref(&self, _arg: &i32) {}
456}
457            "#,
458            r#"
459fn main() {
460    Test.call_by_ref(&123);
461}
462struct Test;
463impl Test {
464    fn call_by_ref(&self, _arg: &i32) {}
465}
466            "#,
467        );
468    }
469
470    #[test]
471    fn add_reference_to_let_stmt() {
472        check_fix(
473            r#"
474fn main() {
475    let test: &i32 = $0123;
476}
477            "#,
478            r#"
479fn main() {
480    let test: &i32 = &123;
481}
482            "#,
483        );
484    }
485
486    #[test]
487    fn add_reference_to_macro_call() {
488        check_fix(
489            r#"
490macro_rules! thousand {
491    () => {
492        1000_u64
493    };
494}
495fn test(_foo: &u64) {}
496fn main() {
497    test($0thousand!());
498}
499            "#,
500            r#"
501macro_rules! thousand {
502    () => {
503        1000_u64
504    };
505}
506fn test(_foo: &u64) {}
507fn main() {
508    test(&thousand!());
509}
510            "#,
511        );
512    }
513
514    #[test]
515    fn add_mutable_reference_to_let_stmt() {
516        check_fix(
517            r#"
518fn main() {
519    let _test: &mut i32 = $0123;
520}
521            "#,
522            r#"
523fn main() {
524    let _test: &mut i32 = &mut 123;
525}
526            "#,
527        );
528    }
529
530    #[test]
531    fn const_generic_type_mismatch() {
532        check_diagnostics(
533            r#"
534            pub struct Rate<const N: u32>;
535            fn f<const N: u64>() -> Rate<N> { // FIXME: add some error
536                loop {}
537            }
538            fn run(_t: Rate<5>) {
539            }
540            fn main() {
541                run(f())
542            }
543"#,
544        );
545    }
546
547    #[test]
548    fn const_generic_unknown() {
549        check_diagnostics(
550            r#"
551            pub struct Rate<T, const NOM: u32, const DENOM: u32>(T);
552            fn run(_t: Rate<u32, 1, 1>) {
553            }
554            fn main() {
555                run(Rate::<_, _, _>(5));
556            }
557"#,
558        );
559    }
560
561    #[test]
562    fn wrap_return_type() {
563        check_fix(
564            r#"
565//- minicore: option, result
566fn div(x: i32, y: i32) -> Result<i32, ()> {
567    if y == 0 {
568        return Err(());
569    }
570    x / y$0
571}
572"#,
573            r#"
574fn div(x: i32, y: i32) -> Result<i32, ()> {
575    if y == 0 {
576        return Err(());
577    }
578    Ok(x / y)
579}
580"#,
581        );
582
583        check_fix(
584            r#"
585//- minicore: option, result
586macro_rules! identity { ($($t:tt)*) => ($($t)*) }
587identity! {
588    fn div(x: i32, y: i32) -> Result<i32, ()> {
589        if y == 0 {
590            return Err(());
591        }
592        x / y$0
593    }
594}
595"#,
596            r#"
597macro_rules! identity { ($($t:tt)*) => ($($t)*) }
598identity! {
599    fn div(x: i32, y: i32) -> Result<i32, ()> {
600        if y == 0 {
601            return Err(());
602        }
603        Ok(x / y)
604    }
605}
606"#,
607        );
608    }
609
610    #[test]
611    fn wrap_return_type_option() {
612        check_fix(
613            r#"
614//- minicore: option, result
615fn div(x: i32, y: i32) -> Option<i32> {
616    if y == 0 {
617        return None;
618    }
619    x / y$0
620}
621"#,
622            r#"
623fn div(x: i32, y: i32) -> Option<i32> {
624    if y == 0 {
625        return None;
626    }
627    Some(x / y)
628}
629"#,
630        );
631    }
632
633    #[test]
634    fn wrap_return_type_option_tails() {
635        check_fix(
636            r#"
637//- minicore: option, result
638fn div(x: i32, y: i32) -> Option<i32> {
639    if y == 0 {
640        Some(0)
641    } else if true {
642        100$0
643    } else {
644        None
645    }
646}
647"#,
648            r#"
649fn div(x: i32, y: i32) -> Option<i32> {
650    if y == 0 {
651        Some(0)
652    } else if true {
653        Some(100)
654    } else {
655        None
656    }
657}
658"#,
659        );
660    }
661
662    #[test]
663    fn wrap_return_type_handles_generic_functions() {
664        check_fix(
665            r#"
666//- minicore: option, result
667fn div<T>(x: T) -> Result<T, i32> {
668    if x == 0 {
669        return Err(7);
670    }
671    $0x
672}
673"#,
674            r#"
675fn div<T>(x: T) -> Result<T, i32> {
676    if x == 0 {
677        return Err(7);
678    }
679    Ok(x)
680}
681"#,
682        );
683    }
684
685    #[test]
686    fn wrap_return_type_handles_type_aliases() {
687        check_fix(
688            r#"
689//- minicore: option, result
690type MyResult<T> = Result<T, ()>;
691
692fn div(x: i32, y: i32) -> MyResult<i32> {
693    if y == 0 {
694        return Err(());
695    }
696    x $0/ y
697}
698"#,
699            r#"
700type MyResult<T> = Result<T, ()>;
701
702fn div(x: i32, y: i32) -> MyResult<i32> {
703    if y == 0 {
704        return Err(());
705    }
706    Ok(x / y)
707}
708"#,
709        );
710    }
711
712    #[test]
713    fn wrapped_unit_as_block_tail_expr() {
714        check_fix(
715            r#"
716//- minicore: result
717fn foo() -> Result<(), ()> {
718    foo();
719}$0
720            "#,
721            r#"
722fn foo() -> Result<(), ()> {
723    foo();
724    Ok(())
725}
726            "#,
727        );
728
729        check_fix(
730            r#"
731//- minicore: result
732fn foo() -> Result<(), ()> {}$0
733            "#,
734            r#"
735fn foo() -> Result<(), ()> {
736    Ok(())
737}
738            "#,
739        );
740
741        check_fix(
742            r#"
743//- minicore: result, iterator
744fn foo() -> Result<(), ()> {
745    for _ in 0..5 {}$0
746}
747            "#,
748            r#"
749fn foo() -> Result<(), ()> {
750    for _ in 0..5 {}
751    Ok(())
752}
753            "#,
754        );
755    }
756
757    #[test]
758    fn wrapped_unit_as_return_expr() {
759        check_fix_with_disabled(
760            r#"
761//- minicore: result
762fn foo(b: bool) -> Result<(), String> {
763    if b {
764        return$0;
765    }
766
767    Err("oh dear".to_owned())
768}"#,
769            r#"
770fn foo(b: bool) -> Result<(), String> {
771    if b {
772        return Ok(());
773    }
774
775    Err("oh dear".to_owned())
776}"#,
777            &["E0599"],
778        );
779    }
780
781    #[test]
782    fn wrap_in_const_and_static() {
783        check_fix(
784            r#"
785//- minicore: option, result
786static A: Option<()> = {($0)};
787            "#,
788            r#"
789static A: Option<()> = {Some(())};
790            "#,
791        );
792        check_fix(
793            r#"
794//- minicore: option, result
795const _: Option<()> = {($0)};
796            "#,
797            r#"
798const _: Option<()> = {Some(())};
799            "#,
800        );
801    }
802
803    #[test]
804    fn wrap_return_type_not_applicable_when_expr_type_does_not_match_ok_type() {
805        check_no_fix(
806            r#"
807//- minicore: option, result
808fn foo() -> Result<(), i32> { 0$0 }
809"#,
810        );
811    }
812
813    #[test]
814    fn wrap_return_type_not_applicable_when_return_type_is_not_result_or_option() {
815        check_no_fix(
816            r#"
817//- minicore: option, result
818enum SomeOtherEnum { Ok(i32), Err(String) }
819
820fn foo() -> SomeOtherEnum { 0$0 }
821"#,
822        );
823    }
824
825    #[test]
826    fn unwrap_return_type() {
827        check_fix_with_disabled(
828            r#"
829//- minicore: option, result
830fn div(x: i32, y: i32) -> i32 {
831    if y == 0 {
832        panic!();
833    }
834    Ok(x / y)$0
835}
836"#,
837            r#"
838fn div(x: i32, y: i32) -> i32 {
839    if y == 0 {
840        panic!();
841    }
842    x / y
843}
844"#,
845            &["E0282"],
846        );
847    }
848
849    #[test]
850    fn unwrap_return_type_option() {
851        check_fix(
852            r#"
853//- minicore: option, result
854fn div(x: i32, y: i32) -> i32 {
855    if y == 0 {
856        panic!();
857    }
858    Some(x / y)$0
859}
860"#,
861            r#"
862fn div(x: i32, y: i32) -> i32 {
863    if y == 0 {
864        panic!();
865    }
866    x / y
867}
868"#,
869        );
870    }
871
872    #[test]
873    fn unwrap_return_type_option_tails() {
874        check_fix(
875            r#"
876//- minicore: option, result
877fn div(x: i32, y: i32) -> i32 {
878    if y == 0 {
879        42
880    } else if true {
881        Some(100)$0
882    } else {
883        0
884    }
885}
886"#,
887            r#"
888fn div(x: i32, y: i32) -> i32 {
889    if y == 0 {
890        42
891    } else if true {
892        100
893    } else {
894        0
895    }
896}
897"#,
898        );
899    }
900
901    #[test]
902    fn unwrap_return_type_option_tail_unit() {
903        check_fix_with_disabled(
904            r#"
905//- minicore: option, result
906fn div(x: i32, y: i32) {
907    if y == 0 {
908        panic!();
909    }
910
911    Ok(())$0
912}
913"#,
914            r#"
915fn div(x: i32, y: i32) {
916    if y == 0 {
917        panic!();
918    }
919}
920"#,
921            &["E0282"],
922        );
923    }
924
925    #[test]
926    fn unwrap_return_type_handles_generic_functions() {
927        check_fix_with_disabled(
928            r#"
929//- minicore: option, result
930fn div<T>(x: T) -> T {
931    if x == 0 {
932        panic!();
933    }
934    $0Ok(x)
935}
936"#,
937            r#"
938fn div<T>(x: T) -> T {
939    if x == 0 {
940        panic!();
941    }
942    x
943}
944"#,
945            &["E0282"],
946        );
947    }
948
949    #[test]
950    fn unwrap_return_type_handles_type_aliases() {
951        check_fix_with_disabled(
952            r#"
953//- minicore: option, result
954type MyResult<T> = T;
955
956fn div(x: i32, y: i32) -> MyResult<i32> {
957    if y == 0 {
958        panic!();
959    }
960    Ok(x $0/ y)
961}
962"#,
963            r#"
964type MyResult<T> = T;
965
966fn div(x: i32, y: i32) -> MyResult<i32> {
967    if y == 0 {
968        panic!();
969    }
970    x / y
971}
972"#,
973            &["E0282"],
974        );
975    }
976
977    #[test]
978    fn unwrap_tail_expr() {
979        check_fix_with_disabled(
980            r#"
981//- minicore: result
982fn foo() -> () {
983    println!("Hello, world!");
984    Ok(())$0
985}
986            "#,
987            r#"
988fn foo() -> () {
989    println!("Hello, world!");
990}
991            "#,
992            &["E0282"],
993        );
994    }
995
996    #[test]
997    fn unwrap_to_empty_block() {
998        check_fix_with_disabled(
999            r#"
1000//- minicore: result
1001fn foo() -> () {
1002    Ok(())$0
1003}
1004            "#,
1005            r#"
1006fn foo() -> () {}
1007            "#,
1008            &["E0282"],
1009        );
1010    }
1011
1012    #[test]
1013    fn unwrap_to_return_expr() {
1014        check_has_fix(
1015            r#"
1016//- minicore: result
1017fn foo(b: bool) -> () {
1018    if b {
1019        return $0Ok(());
1020    }
1021
1022    panic!("oh dear");
1023}"#,
1024            r#"
1025fn foo(b: bool) -> () {
1026    if b {
1027        return;
1028    }
1029
1030    panic!("oh dear");
1031}"#,
1032        );
1033    }
1034
1035    #[test]
1036    fn unwrap_in_const_and_static() {
1037        check_fix(
1038            r#"
1039//- minicore: option, result
1040static A: () = {Some(($0))};
1041            "#,
1042            r#"
1043static A: () = {};
1044            "#,
1045        );
1046        check_fix(
1047            r#"
1048//- minicore: option, result
1049const _: () = {Some(($0))};
1050            "#,
1051            r#"
1052const _: () = {};
1053            "#,
1054        );
1055    }
1056
1057    #[test]
1058    fn unwrap_return_type_not_applicable_when_inner_type_does_not_match_return_type() {
1059        check_no_fix(
1060            r#"
1061//- minicore: result
1062fn foo() -> i32 { $0Ok(()) }
1063"#,
1064        );
1065    }
1066
1067    #[test]
1068    fn unwrap_return_type_not_applicable_when_wrapper_type_is_not_result_or_option() {
1069        check_no_fix(
1070            r#"
1071//- minicore: option, result
1072enum SomeOtherEnum { Ok(i32), Err(String) }
1073
1074fn foo() -> i32 { SomeOtherEnum::Ok($042) }
1075"#,
1076        );
1077    }
1078
1079    #[test]
1080    fn remove_semicolon() {
1081        check_fix(r#"fn f() -> i32 { 92$0; }"#, r#"fn f() -> i32 { 92 }"#);
1082    }
1083
1084    #[test]
1085    fn str_ref_to_owned() {
1086        check_fix(
1087            r#"
1088struct String;
1089
1090fn test() -> String {
1091    "a"$0
1092}
1093            "#,
1094            r#"
1095struct String;
1096
1097fn test() -> String {
1098    "a".to_owned()
1099}
1100            "#,
1101        );
1102
1103        check_fix(
1104            r#"
1105macro_rules! identity { ($($t:tt)*) => ($($t)*) }
1106struct String;
1107
1108identity! {
1109    fn test() -> String {
1110        "a"$0
1111    }
1112}
1113            "#,
1114            r#"
1115macro_rules! identity { ($($t:tt)*) => ($($t)*) }
1116struct String;
1117
1118identity! {
1119    fn test() -> String {
1120        "a".to_owned()
1121    }
1122}
1123            "#,
1124        );
1125    }
1126
1127    #[test]
1128    fn type_mismatch_range_adjustment() {
1129        cov_mark::check!(type_mismatch_range_adjustment);
1130        check_diagnostics(
1131            r#"
1132fn f() -> i32 {
1133    let x = 1;
1134    let y = 2;
1135    let _ = x + y;
1136  }
1137//^ error: expected i32, found ()
1138
1139fn g() -> i32 {
1140    while true {}
1141} //^^^^^ error: expected i32, found ()
1142
1143struct S;
1144impl S { fn foo(&self) -> &S { self } }
1145fn h() {
1146    let _: i32 = S.foo().foo().foo();
1147}                            //^^^ error: expected i32, found &S
1148"#,
1149        );
1150    }
1151
1152    #[test]
1153    fn unknown_type_in_function_signature() {
1154        check_diagnostics(
1155            r#"
1156struct X<T>(T);
1157
1158fn foo(_x: X<Unknown>) {}
1159fn test1() {
1160    // Unknown might be `i32`, so we should not emit type mismatch here.
1161    foo(X(42));
1162}
1163fn test2() {
1164    foo(42);
1165      //^^ error: expected X<{unknown}>, found i32
1166}
1167"#,
1168        );
1169    }
1170
1171    #[test]
1172    fn evaluate_const_generics_in_types() {
1173        check_diagnostics(
1174            r#"
1175pub const ONE: usize = 1;
1176
1177pub struct Inner<const P: usize>();
1178
1179pub struct Outer {
1180    pub inner: Inner<ONE>,
1181}
1182
1183fn main() {
1184    _ = Outer {
1185        inner: Inner::<2>(),
1186             //^^^^^^^^^^^^ error: expected Inner<1>, found Inner<2>
1187    };
1188}
1189"#,
1190        );
1191    }
1192
1193    #[test]
1194    fn type_mismatch_pat_smoke_test() {
1195        check_diagnostics(
1196            r#"
1197fn f() {
1198    let &() = &mut ();
1199      //^^^ error: expected &mut (), found &()
1200    match &() {
1201        &9 => ()
1202       //^ error: expected (), found i32
1203    }
1204}
1205"#,
1206        );
1207    }
1208
1209    #[test]
1210    fn regression_14768() {
1211        check_diagnostics(
1212            r#"
1213//- minicore: derive, fmt, slice, coerce_unsized, builtin_impls
1214use core::fmt::Debug;
1215
1216#[derive(Debug)]
1217struct Foo(u8, u16, [u8]);
1218
1219#[derive(Debug)]
1220struct Bar {
1221    f1: u8,
1222    f2: &[u16],
1223    f3: dyn Debug,
1224}
1225"#,
1226        );
1227    }
1228
1229    #[test]
1230    fn trait_upcast_ok() {
1231        check_diagnostics(
1232            r#"
1233//- minicore: coerce_unsized
1234trait A: B {}
1235trait B {}
1236
1237fn test(a: &dyn A) -> &dyn B {
1238    a
1239}
1240"#,
1241        );
1242    }
1243
1244    #[test]
1245    fn trait_upcast_err() {
1246        check_diagnostics(
1247            r#"
1248//- minicore: coerce_unsized
1249trait A {} // `A` does not have `B` as a supertrait, so no upcast :c
1250trait B {}
1251
1252fn test(a: &dyn A) -> &dyn B {
1253    a
1254  //^💡 error: expected &(dyn B + 'static), found &(dyn A + 'static)
1255}
1256"#,
1257        );
1258    }
1259
1260    #[test]
1261    fn return_no_value() {
1262        check_diagnostics_with_disabled(
1263            r#"
1264fn f() -> i32 {
1265    return;
1266 // ^^^^^^ error: expected i32, found ()
1267    0
1268}
1269fn g() { return; }
1270"#,
1271            &["needless_return"],
1272        );
1273    }
1274
1275    #[test]
1276    fn smoke_test_inner_items() {
1277        check_diagnostics(
1278            r#"
1279fn f() {
1280    fn inner() -> i32 {
1281        return;
1282     // ^^^^^^ error: expected i32, found ()
1283        0
1284    }
1285}
1286"#,
1287        );
1288    }
1289
1290    #[test]
1291    fn regression_17585() {
1292        check_diagnostics(
1293            r#"
1294fn f() {
1295    let (_, _, _, ..) = (true, 42);
1296     // ^^^^^^^^^^^^^ error: expected (bool, i32), found (bool, i32, {unknown})
1297}
1298"#,
1299        );
1300    }
1301
1302    #[test]
1303    fn complex_enum_variant_non_ref_pat() {
1304        check_diagnostics(
1305            r#"
1306enum Enum { Variant }
1307
1308trait Trait {
1309    type Assoc;
1310}
1311impl Trait for () {
1312    type Assoc = Enum;
1313}
1314
1315fn foo(v: &Enum) {
1316    let <Enum>::Variant = v;
1317    let <() as Trait>::Assoc::Variant = v;
1318}
1319    "#,
1320        );
1321    }
1322
1323    #[test]
1324    fn regression_19844() {
1325        check_diagnostics(
1326            r#"
1327fn main() {
1328    struct S {}
1329    enum E { V() }
1330    let E::V() = &S {};
1331     // ^^^^^^ error: expected S, found E
1332}
1333"#,
1334        );
1335    }
1336
1337    #[test]
1338    fn test_ignore_unknown_mismatch() {
1339        check_diagnostics(
1340            r#"
1341pub trait Foo {
1342    type Out;
1343}
1344impl Foo for [i32; 1] {
1345    type Out = ();
1346}
1347pub fn foo<T: Foo>(_: T) -> (T::Out,) { loop { } }
1348
1349fn main() {
1350    let _x = foo(2);
1351     // ^^ error: type annotations needed
1352          // ^^^ error: the trait bound `i32: Foo` is not satisfied
1353}
1354"#,
1355        );
1356    }
1357}