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
19pub(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 let mut builder = TextEdit::builder();
131 let block_indent = block.indent_level();
132
133 if block.statements().count() == 0 {
134 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 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 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 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}