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