From 5212a66fd0c1fb182230e33570ccd263aeedcaa3 Mon Sep 17 00:00:00 2001 From: sgasho Date: Tue, 30 Sep 2025 22:21:31 +0900 Subject: [PATCH] fix: check_expr_if to point to a more accurate location of the compilation error in some cases --- compiler/rustc_hir_typeck/src/_if.rs | 313 ++++++++++++++++++ compiler/rustc_hir_typeck/src/expr.rs | 72 +--- compiler/rustc_hir_typeck/src/lib.rs | 1 + .../src/error_reporting/infer/mod.rs | 48 +-- .../ui/expr/if/if-else-chain-missing-else.rs | 11 +- .../expr/if/if-else-chain-missing-else.stderr | 12 +- ...else-if-incompatible-types-issue-146190.rs | 15 + ...-if-incompatible-types-issue-146190.stderr | 22 ++ tests/ui/inference/deref-suggestion.rs | 13 +- tests/ui/inference/deref-suggestion.stderr | 76 ++--- .../ui/typeck/consider-borrowing-141810-1.rs | 3 +- .../typeck/consider-borrowing-141810-1.stderr | 26 +- .../ui/typeck/consider-borrowing-141810-2.rs | 1 - .../typeck/consider-borrowing-141810-2.stderr | 10 +- .../typeck/consider-borrowing-141810-3.stderr | 10 +- 15 files changed, 464 insertions(+), 169 deletions(-) create mode 100644 compiler/rustc_hir_typeck/src/_if.rs create mode 100644 tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.rs create mode 100644 tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.stderr diff --git a/compiler/rustc_hir_typeck/src/_if.rs b/compiler/rustc_hir_typeck/src/_if.rs new file mode 100644 index 0000000000000..2e0ecdab83ca6 --- /dev/null +++ b/compiler/rustc_hir_typeck/src/_if.rs @@ -0,0 +1,313 @@ +use rustc_errors::Diag; +use rustc_hir::{self as hir, HirId}; +use rustc_infer::traits; +use rustc_middle::ty::{Ty, TypeVisitableExt}; +use rustc_span::{ErrorGuaranteed, Span}; +use smallvec::SmallVec; + +use crate::coercion::{CoerceMany, DynamicCoerceMany}; +use crate::{Diverges, Expectation, FnCtxt, bug}; + +#[derive(Clone, Debug)] +struct BranchBody<'tcx> { + expr: &'tcx hir::Expr<'tcx>, + ty: Ty<'tcx>, + diverges: Diverges, + span: Span, +} + +#[derive(Clone, Debug)] +struct IfBranch<'tcx> { + if_expr: &'tcx hir::Expr<'tcx>, + cond_diverges: Diverges, + body: BranchBody<'tcx>, +} + +#[derive(Default, Debug)] +struct IfChain<'tcx> { + branches: SmallVec<[IfBranch<'tcx>; 4]>, + cond_error: Option, +} + +enum IfChainTail<'tcx> { + FinalElse(BranchBody<'tcx>), + Missing(&'tcx hir::Expr<'tcx>), +} + +const RECENT_BRANCH_HISTORY_LIMIT: usize = 5; + +#[derive(Default)] +struct RecentBranchTypeHistory<'tcx> { + entries: SmallVec<[(Ty<'tcx>, Span); 4]>, +} + +impl<'tcx> RecentBranchTypeHistory<'tcx> { + fn diagnostic_snapshot(&self) -> SmallVec<[(Ty<'tcx>, Span); 4]> { + self.entries.clone() + } + + fn record(&mut self, ty: Ty<'tcx>, span: Span) { + if ty.is_never() { + return; + } + + self.entries.push((ty, span)); + if self.entries.len() > RECENT_BRANCH_HISTORY_LIMIT { + self.entries.remove(0); + } + } +} + +impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + pub(crate) fn check_expr_if( + &self, + expr_id: HirId, + sp: Span, + orig_expected: Expectation<'tcx>, + ) -> Ty<'tcx> { + let root_if_expr = self.tcx.hir_expect_expr(expr_id); + let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp); + + let initial_diverges = self.diverges.get(); + + let (chain, tail) = self.collect_if_chain(root_if_expr, expected); + + let terminal_else_expr = match &tail { + IfChainTail::FinalElse(else_branch) => Some(else_branch.expr), + IfChainTail::Missing(last_if_expr) => Some(*last_if_expr), + }; + + let coerce_to_ty = expected.coercion_target_type(self, sp); + let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); + + let tail_defines_return_position_impl_trait = + self.return_position_impl_trait_from_match_expectation(orig_expected); + let mut recent_branch_types = RecentBranchTypeHistory::default(); + + for (idx, branch) in chain.branches.iter().enumerate() { + if idx > 0 { + let merged_ty = coerce.merged_ty(); + self.ensure_if_branch_type(branch.if_expr.hir_id, merged_ty); + } + + let branch_body = &branch.body; + let next_else_expr = + chain.branches.get(idx + 1).map(|next| next.if_expr).or(terminal_else_expr); + let mut branch_cause = if let Some(next_else_expr) = next_else_expr { + let prev_branch = + if idx > 0 { chain.branches.get(idx - 1).unwrap_or(branch) } else { branch }; + self.if_cause( + prev_branch.if_expr.hir_id, + next_else_expr, + tail_defines_return_position_impl_trait, + ) + } else { + self.misc(branch_body.span) + }; + let diag_info = recent_branch_types.diagnostic_snapshot(); + let cause_span = + if idx == 0 { Some(root_if_expr.span) } else { Some(branch_body.span) }; + + self.coerce_if_arm( + &mut coerce, + &mut branch_cause, + branch_body.expr, + branch_body.ty, + cause_span, + branch_body.span, + diag_info, + ); + + recent_branch_types.record(branch_body.ty, branch_body.span); + } + + let mut tail_diverges = match tail { + IfChainTail::FinalElse(else_branch) => { + let mut else_cause = self.if_cause( + expr_id, + else_branch.expr, + tail_defines_return_position_impl_trait, + ); + let diag_info = recent_branch_types.diagnostic_snapshot(); + self.coerce_if_arm( + &mut coerce, + &mut else_cause, + else_branch.expr, + else_branch.ty, + None, + else_branch.span, + diag_info, + ); + recent_branch_types.record(else_branch.ty, else_branch.span); + + else_branch.diverges + } + IfChainTail::Missing(last_if_expr) => { + let hir::ExprKind::If(tail_cond, tail_then, _) = last_if_expr.kind else { + bug!("expected `if` expression, found {:#?}", last_if_expr); + }; + self.if_fallback_coercion(last_if_expr.span, tail_cond, tail_then, &mut coerce); + Diverges::Maybe + } + }; + + for branch in chain.branches.iter().rev() { + tail_diverges = branch.cond_diverges | (branch.body.diverges & tail_diverges); + } + self.diverges.set(initial_diverges | tail_diverges); + + let result_ty = coerce.complete(self); + + let final_ty = if let Some(guar) = chain.cond_error { + Ty::new_error(self.tcx, guar) + } else { + result_ty + }; + + for branch in chain.branches.iter().skip(1) { + self.overwrite_if_branch_type(branch.if_expr.hir_id, final_ty); + } + if let Err(guar) = final_ty.error_reported() { + self.set_tainted_by_errors(guar); + } + + final_ty + } + + fn coerce_if_arm( + &self, + coerce: &mut DynamicCoerceMany<'tcx>, + cause: &mut traits::ObligationCause<'tcx>, + expr: &'tcx hir::Expr<'tcx>, + ty: Ty<'tcx>, + cause_span: Option, + body_span: Span, + prior_branches: SmallVec<[(Ty<'tcx>, Span); 4]>, + ) { + if let Some(span) = cause_span { + cause.span = span; + } + coerce.coerce_inner( + self, + cause, + Some(expr), + ty, + move |err| self.explain_if_branch_mismatch(err, body_span, &prior_branches), + false, + ); + } + + fn check_if_condition( + &self, + cond_expr: &'tcx hir::Expr<'tcx>, + then_span: Span, + ) -> (Ty<'tcx>, Diverges) { + let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); + self.warn_if_unreachable( + cond_expr.hir_id, + then_span, + "block in `if` or `while` expression", + ); + let cond_diverges = self.take_diverges(); + (cond_ty, cond_diverges) + } + + fn collect_if_chain( + &self, + mut current_if: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> (IfChain<'tcx>, IfChainTail<'tcx>) { + let mut chain: IfChain<'tcx> = IfChain::default(); + + loop { + let Some(else_expr) = self.collect_if_branch(current_if, expected, &mut chain) else { + return (chain, IfChainTail::Missing(current_if)); + }; + + if let hir::ExprKind::If(..) = else_expr.kind { + current_if = else_expr; + continue; + } + + return (chain, IfChainTail::FinalElse(self.collect_final_else(else_expr, expected))); + } + } + + fn collect_if_branch( + &self, + if_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + chain: &mut IfChain<'tcx>, + ) -> Option<&'tcx hir::Expr<'tcx>> { + let hir::ExprKind::If(cond_expr, then_expr, opt_else_expr) = if_expr.kind else { + bug!("expected `if` expression, found {:#?}", if_expr); + }; + + let (cond_ty, cond_diverges) = self.check_if_condition(cond_expr, then_expr.span); + if let Err(guar) = cond_ty.error_reported() { + chain.cond_error.get_or_insert(guar); + } + let branch_body = self.check_branch_body(then_expr, expected); + + chain.branches.push(IfBranch { if_expr, cond_diverges, body: branch_body }); + + opt_else_expr + } + + fn collect_final_else( + &self, + else_expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> BranchBody<'tcx> { + self.check_branch_body(else_expr, expected) + } + + fn reset_diverges_to_maybe(&self) { + self.diverges.set(Diverges::Maybe); + } + + fn take_diverges(&self) -> Diverges { + let diverges = self.diverges.get(); + self.reset_diverges_to_maybe(); + diverges + } + + fn ensure_if_branch_type(&self, hir_id: HirId, ty: Ty<'tcx>) { + let mut typeck = self.typeck_results.borrow_mut(); + let mut node_ty = typeck.node_types_mut(); + node_ty.entry(hir_id).or_insert(ty); + } + + fn overwrite_if_branch_type(&self, hir_id: HirId, ty: Ty<'tcx>) { + let mut typeck = self.typeck_results.borrow_mut(); + let mut node_ty = typeck.node_types_mut(); + node_ty.insert(hir_id, ty); + } + + fn check_branch_body( + &self, + expr: &'tcx hir::Expr<'tcx>, + expected: Expectation<'tcx>, + ) -> BranchBody<'tcx> { + self.reset_diverges_to_maybe(); + let ty = self.check_expr_with_expectation(expr, expected); + let diverges = self.take_diverges(); + let span = self.find_block_span_from_hir_id(expr.hir_id); + BranchBody { expr, ty, diverges, span } + } + + fn explain_if_branch_mismatch( + &self, + err: &mut Diag<'_>, + branch_span: Span, + prior_branches: &[(Ty<'tcx>, Span)], + ) { + let Some(&(.., prior_span)) = + prior_branches.iter().rev().find(|&&(_, span)| span != branch_span) + else { + return; + }; + + err.span_label(prior_span, "expected because of this"); + } +} diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 7adbee7ff2855..2e2437ef8cd6d 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -40,7 +40,7 @@ use tracing::{debug, instrument, trace}; use {rustc_ast as ast, rustc_hir as hir}; use crate::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::coercion::{CoerceMany, DynamicCoerceMany}; +use crate::coercion::CoerceMany; use crate::errors::{ AddressOfTemporaryTaken, BaseExpressionDoubleDot, BaseExpressionDoubleDotAddExpr, BaseExpressionDoubleDotRemove, CantDereference, FieldMultiplySpecifiedInInitializer, @@ -584,9 +584,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.demand_eqtype(e.span, ascribed_ty, ty); ascribed_ty } - ExprKind::If(cond, then_expr, opt_else_expr) => { - self.check_expr_if(expr.hir_id, cond, then_expr, opt_else_expr, expr.span, expected) - } + ExprKind::If(..) => self.check_expr_if(expr.hir_id, expr.span, expected), ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), ExprKind::Array(args) => self.check_expr_array(args, expected, expr), ExprKind::ConstBlock(ref block) => self.check_expr_const_block(block, expected), @@ -1341,72 +1339,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - // A generic function for checking the 'then' and 'else' clauses in an 'if' - // or 'if-else' expression. - fn check_expr_if( - &self, - expr_id: HirId, - cond_expr: &'tcx hir::Expr<'tcx>, - then_expr: &'tcx hir::Expr<'tcx>, - opt_else_expr: Option<&'tcx hir::Expr<'tcx>>, - sp: Span, - orig_expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); - - self.warn_if_unreachable( - cond_expr.hir_id, - then_expr.span, - "block in `if` or `while` expression", - ); - - let cond_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - let expected = orig_expected.try_structurally_resolve_and_adjust_for_branches(self, sp); - let then_ty = self.check_expr_with_expectation(then_expr, expected); - let then_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - // We've already taken the expected type's preferences - // into account when typing the `then` branch. To figure - // out the initial shot at a LUB, we thus only consider - // `expected` if it represents a *hard* constraint - // (`only_has_type`); otherwise, we just go with a - // fresh type variable. - let coerce_to_ty = expected.coercion_target_type(self, sp); - let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); - - coerce.coerce(self, &self.misc(sp), then_expr, then_ty); - - if let Some(else_expr) = opt_else_expr { - let else_ty = self.check_expr_with_expectation(else_expr, expected); - let else_diverges = self.diverges.get(); - - let tail_defines_return_position_impl_trait = - self.return_position_impl_trait_from_match_expectation(orig_expected); - let if_cause = - self.if_cause(expr_id, else_expr, tail_defines_return_position_impl_trait); - - coerce.coerce(self, &if_cause, else_expr, else_ty); - - // We won't diverge unless both branches do (or the condition does). - self.diverges.set(cond_diverges | then_diverges & else_diverges); - } else { - self.if_fallback_coercion(sp, cond_expr, then_expr, &mut coerce); - - // If the condition is false we can't diverge. - self.diverges.set(cond_diverges); - } - - let result_ty = coerce.complete(self); - if let Err(guar) = cond_ty.error_reported() { - Ty::new_error(self.tcx, guar) - } else { - result_ty - } - } - /// Type check assignment expression `expr` of form `lhs = rhs`. /// The expected type is `()` and is passed to the function for the purposes of diagnostics. fn check_expr_assign( diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 129de32fd4adb..4fde31859da80 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -8,6 +8,7 @@ #![feature(never_type)] // tidy-alphabetical-end +mod _if; mod _match; mod autoderef; mod callee; diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index e18e294635b52..3feabf948464c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -622,25 +622,33 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return; }; let then_span = self.find_block_span_from_hir_id(then_expr.hir_id); - let then_ty = self - .typeck_results - .as_ref() - .expect("if expression only expected inside FnCtxt") - .expr_ty(then_expr); let else_span = self.find_block_span_from_hir_id(else_expr.hir_id); - let else_ty = self + + let typeck_results = self .typeck_results .as_ref() - .expect("if expression only expected inside FnCtxt") - .expr_ty(else_expr); - if let hir::ExprKind::If(_cond, _then, None) = else_expr.kind + .expect("if expression only expected inside FnCtxt"); + + let then_ty = typeck_results.expr_ty(then_expr); + let mut else_ty = typeck_results.expr_ty_opt(else_expr); + + if else_ty.is_none() + && let Some(exp_found) = exp_found + { + let exp_found = ty::error::ExpectedFound { + expected: self.resolve_vars_if_possible(exp_found.expected), + found: self.resolve_vars_if_possible(exp_found.found), + }; + else_ty.get_or_insert(exp_found.found); + } + + if let (Some(else_ty), hir::ExprKind::If(.., None)) = (else_ty, &else_expr.kind) && else_ty.is_unit() { // Account for `let x = if a { 1 } else if b { 2 };` err.note("`if` expressions without `else` evaluate to `()`"); err.note("consider adding an `else` block that evaluates to the expected type"); } - err.span_label(then_span, "expected because of this"); let outer_span = if self.tcx.sess.source_map().is_multiline(expr_span) { if then_span.hi() == expr_span.hi() || else_span.hi() == expr_span.hi() { @@ -667,15 +675,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } else { else_expr.hir_id }; - if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( - Some(then_id), - then_ty, - then_span, - Some(else_id), - else_ty, - else_span, - ) { - err.subdiagnostic(subdiag); + if let Some(else_ty) = else_ty { + if let Some(subdiag) = self.suggest_remove_semi_or_return_binding( + Some(then_id), + then_ty, + then_span, + Some(else_id), + else_ty, + else_span, + ) { + err.subdiagnostic(subdiag); + } } } ObligationCauseCode::LetElse => { diff --git a/tests/ui/expr/if/if-else-chain-missing-else.rs b/tests/ui/expr/if/if-else-chain-missing-else.rs index 995aac07f2f76..e45939ff7beac 100644 --- a/tests/ui/expr/if/if-else-chain-missing-else.rs +++ b/tests/ui/expr/if/if-else-chain-missing-else.rs @@ -1,5 +1,10 @@ -enum Cause { Cause1, Cause2 } -struct MyErr { x: Cause } +enum Cause { + Cause1, + Cause2, +} +struct MyErr { + x: Cause, +} fn main() { _ = f(); @@ -9,7 +14,7 @@ fn f() -> Result { let res = could_fail(); let x = if let Ok(x) = res { x - } else if let Err(e) = res { //~ ERROR `if` and `else` + } else if let Err(e) = res { //~ ERROR `if` may be missing an `else` clause return Err(e); }; Ok(x) diff --git a/tests/ui/expr/if/if-else-chain-missing-else.stderr b/tests/ui/expr/if/if-else-chain-missing-else.stderr index 6c437120d391d..a21b9e135272d 100644 --- a/tests/ui/expr/if/if-else-chain-missing-else.stderr +++ b/tests/ui/expr/if/if-else-chain-missing-else.stderr @@ -1,10 +1,6 @@ -error[E0308]: `if` and `else` have incompatible types - --> $DIR/if-else-chain-missing-else.rs:12:12 +error[E0317]: `if` may be missing an `else` clause + --> $DIR/if-else-chain-missing-else.rs:17:12 | -LL | let x = if let Ok(x) = res { - | ------------------ `if` and `else` have incompatible types -LL | x - | - expected because of this LL | } else if let Err(e) = res { | ____________^ LL | | return Err(e); @@ -12,8 +8,8 @@ LL | | }; | |_____^ expected `i32`, found `()` | = note: `if` expressions without `else` evaluate to `()` - = note: consider adding an `else` block that evaluates to the expected type + = help: consider adding an `else` block that evaluates to the expected type error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0308`. +For more information about this error, try `rustc --explain E0317`. diff --git a/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.rs b/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.rs new file mode 100644 index 0000000000000..488bb76a9605a --- /dev/null +++ b/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.rs @@ -0,0 +1,15 @@ +fn main() { + let oa = Some(1); + let oa2 = Some(1); + let oa3 = Some(1); + let v = if let Some(a) = oa { + Some(&a) + } else if let Some(a) = oa2 { + &Some(a) //~ ERROR `if` and `else` have incompatible types [E0308] + } else if let Some(a) = oa3 { + &Some(a) + } else { + None + }; + println!("{v:?}"); +} diff --git a/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.stderr b/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.stderr new file mode 100644 index 0000000000000..a846ac521186b --- /dev/null +++ b/tests/ui/expr/if/if-multi-else-if-incompatible-types-issue-146190.stderr @@ -0,0 +1,22 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/if-multi-else-if-incompatible-types-issue-146190.rs:8:9 + | +LL | let v = if let Some(a) = oa { + | ------------------- `if` and `else` have incompatible types +LL | Some(&a) + | -------- expected because of this +LL | } else if let Some(a) = oa2 { +LL | &Some(a) + | ^^^^^^^^ expected `Option<&{integer}>`, found `&Option<{integer}>` + | + = note: expected enum `Option<&{integer}>` + found reference `&Option<{integer}>` +help: try using `.as_ref()` to convert `&Option<{integer}>` to `Option<&{integer}>` + | +LL - &Some(a) +LL + Some(a).as_ref() + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/inference/deref-suggestion.rs b/tests/ui/inference/deref-suggestion.rs index dc39cc9dbffb0..c7e10f9d641ff 100644 --- a/tests/ui/inference/deref-suggestion.rs +++ b/tests/ui/inference/deref-suggestion.rs @@ -1,5 +1,7 @@ macro_rules! borrow { - ($x:expr) => { &$x } + ($x:expr) => { + &$x + }; } fn foo(_: String) {} @@ -39,15 +41,14 @@ fn main() { let u = 3; let s = S { u }; //~^ ERROR mismatched types - let s = S { u: u }; + let s = S { u }; //~^ ERROR mismatched types let i = &4; let r = R { i }; //~^ ERROR mismatched types - let r = R { i: i }; + let r = R { i }; //~^ ERROR mismatched types - let a = &1; let b = &2; let val: i32 = if true { @@ -67,8 +68,8 @@ fn main() { let val = if true { *a } else if true { - //~^ ERROR incompatible types b + //~^ ERROR incompatible types } else { &0 }; @@ -79,6 +80,6 @@ fn main() { let bar = &Foo; if foo == bar { - //~^ ERROR mismatched types + //~^ ERROR mismatched types } } diff --git a/tests/ui/inference/deref-suggestion.stderr b/tests/ui/inference/deref-suggestion.stderr index 027902a9f31e2..0b049340bfc2b 100644 --- a/tests/ui/inference/deref-suggestion.stderr +++ b/tests/ui/inference/deref-suggestion.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:8:9 + --> $DIR/deref-suggestion.rs:10:9 | LL | foo(s); | --- ^ expected `String`, found `&String` @@ -7,7 +7,7 @@ LL | foo(s); | arguments to this function are incorrect | note: function defined here - --> $DIR/deref-suggestion.rs:5:4 + --> $DIR/deref-suggestion.rs:7:4 | LL | fn foo(_: String) {} | ^^^ --------- @@ -17,7 +17,7 @@ LL | foo(s.to_string()); | ++++++++++++ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:14:10 + --> $DIR/deref-suggestion.rs:16:10 | LL | foo3(u); | ---- ^ expected `u32`, found `&u32` @@ -25,7 +25,7 @@ LL | foo3(u); | arguments to this function are incorrect | note: function defined here - --> $DIR/deref-suggestion.rs:12:4 + --> $DIR/deref-suggestion.rs:14:4 | LL | fn foo3(_: u32) {} | ^^^^ ------ @@ -35,7 +35,7 @@ LL | foo3(*u); | + error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:30:9 + --> $DIR/deref-suggestion.rs:32:9 | LL | foo(&"aaa".to_owned()); | --- ^^^^^^^^^^^^^^^^^ expected `String`, found `&String` @@ -43,7 +43,7 @@ LL | foo(&"aaa".to_owned()); | arguments to this function are incorrect | note: function defined here - --> $DIR/deref-suggestion.rs:5:4 + --> $DIR/deref-suggestion.rs:7:4 | LL | fn foo(_: String) {} | ^^^ --------- @@ -54,7 +54,7 @@ LL + foo("aaa".to_owned()); | error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:32:9 + --> $DIR/deref-suggestion.rs:34:9 | LL | foo(&mut "aaa".to_owned()); | --- ^^^^^^^^^^^^^^^^^^^^^ expected `String`, found `&mut String` @@ -62,7 +62,7 @@ LL | foo(&mut "aaa".to_owned()); | arguments to this function are incorrect | note: function defined here - --> $DIR/deref-suggestion.rs:5:4 + --> $DIR/deref-suggestion.rs:7:4 | LL | fn foo(_: String) {} | ^^^ --------- @@ -73,7 +73,7 @@ LL + foo("aaa".to_owned()); | error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:34:10 + --> $DIR/deref-suggestion.rs:36:10 | LL | foo3(borrow!(0)); | ---- ^^^^^^^^^^ expected `u32`, found `&{integer}` @@ -81,13 +81,13 @@ LL | foo3(borrow!(0)); | arguments to this function are incorrect | note: function defined here - --> $DIR/deref-suggestion.rs:12:4 + --> $DIR/deref-suggestion.rs:14:4 | LL | fn foo3(_: u32) {} | ^^^^ ------ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:37:22 + --> $DIR/deref-suggestion.rs:39:22 | LL | assert_eq!(3i32, &3i32); | ^^^^^ expected `i32`, found `&i32` @@ -99,7 +99,7 @@ LL + assert_eq!(3i32, 3i32); | error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:40:17 + --> $DIR/deref-suggestion.rs:42:17 | LL | let s = S { u }; | ^ expected `&u32`, found integer @@ -110,18 +110,18 @@ LL | let s = S { u: &u }; | ++++ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:42:20 + --> $DIR/deref-suggestion.rs:44:17 | -LL | let s = S { u: u }; - | ^ expected `&u32`, found integer +LL | let s = S { u }; + | ^ expected `&u32`, found integer | help: consider borrowing here | LL | let s = S { u: &u }; - | + + | ++++ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:45:17 + --> $DIR/deref-suggestion.rs:47:17 | LL | let r = R { i }; | ^ expected `u32`, found `&{integer}` @@ -132,18 +132,18 @@ LL | let r = R { i: *i }; | ++++ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:47:20 + --> $DIR/deref-suggestion.rs:49:17 | -LL | let r = R { i: i }; - | ^ expected `u32`, found `&{integer}` +LL | let r = R { i }; + | ^ expected `u32`, found `&{integer}` | help: consider dereferencing the borrow | LL | let r = R { i: *i }; - | + + | ++++ error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:56:9 + --> $DIR/deref-suggestion.rs:57:9 | LL | b | ^ expected `i32`, found `&{integer}` @@ -154,7 +154,7 @@ LL | *b | + error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:64:9 + --> $DIR/deref-suggestion.rs:65:9 | LL | b | ^ expected `i32`, found `&{integer}` @@ -165,23 +165,23 @@ LL | *b | + error[E0308]: `if` and `else` have incompatible types - --> $DIR/deref-suggestion.rs:69:12 - | -LL | let val = if true { - | ------- `if` and `else` have incompatible types -LL | *a - | -- expected because of this -LL | } else if true { - | ____________^ -LL | | -LL | | b -LL | | } else { -LL | | &0 -LL | | }; - | |_____^ expected `i32`, found `&{integer}` + --> $DIR/deref-suggestion.rs:71:9 + | +LL | let val = if true { + | ------- `if` and `else` have incompatible types +LL | *a + | -- expected because of this +LL | } else if true { +LL | b + | ^ expected `i32`, found `&{integer}` + | +help: consider dereferencing the borrow + | +LL | *b + | + error[E0308]: mismatched types - --> $DIR/deref-suggestion.rs:81:15 + --> $DIR/deref-suggestion.rs:82:15 | LL | if foo == bar { | --- ^^^ expected `Foo`, found `&Foo` diff --git a/tests/ui/typeck/consider-borrowing-141810-1.rs b/tests/ui/typeck/consider-borrowing-141810-1.rs index 94c2d69091517..3a339717a9751 100644 --- a/tests/ui/typeck/consider-borrowing-141810-1.rs +++ b/tests/ui/typeck/consider-borrowing-141810-1.rs @@ -1,8 +1,9 @@ fn main() { let x = if true { &true - } else if false { //~ ERROR `if` and `else` have incompatible types [E0308] + } else if false { true //~ HELP consider borrowing here + //~^ ERROR `if` and `else` have incompatible types [E0308] } else { true }; diff --git a/tests/ui/typeck/consider-borrowing-141810-1.stderr b/tests/ui/typeck/consider-borrowing-141810-1.stderr index 35ca6793eee0d..d83f58e612669 100644 --- a/tests/ui/typeck/consider-borrowing-141810-1.stderr +++ b/tests/ui/typeck/consider-borrowing-141810-1.stderr @@ -1,24 +1,18 @@ error[E0308]: `if` and `else` have incompatible types - --> $DIR/consider-borrowing-141810-1.rs:4:12 + --> $DIR/consider-borrowing-141810-1.rs:5:9 | -LL | let x = if true { - | ------- `if` and `else` have incompatible types -LL | &true - | ----- expected because of this -LL | } else if false { - | ____________^ -LL | | true -LL | | } else { -LL | | true -LL | | }; - | |_____^ expected `&bool`, found `bool` +LL | let x = if true { + | ------- `if` and `else` have incompatible types +LL | &true + | ----- expected because of this +LL | } else if false { +LL | true + | ^^^^ expected `&bool`, found `bool` | help: consider borrowing here | -LL ~ &true -LL | } else { -LL ~ &true - | +LL | &true + | + error: aborting due to 1 previous error diff --git a/tests/ui/typeck/consider-borrowing-141810-2.rs b/tests/ui/typeck/consider-borrowing-141810-2.rs index e32e689efb7e5..daa1e9ad28dd2 100644 --- a/tests/ui/typeck/consider-borrowing-141810-2.rs +++ b/tests/ui/typeck/consider-borrowing-141810-2.rs @@ -4,5 +4,4 @@ fn main() { } else if false { //~ ERROR `if` and `else` have incompatible types [E0308] } else { }; - } diff --git a/tests/ui/typeck/consider-borrowing-141810-2.stderr b/tests/ui/typeck/consider-borrowing-141810-2.stderr index 44ecb5a4a945a..cb527f89714ca 100644 --- a/tests/ui/typeck/consider-borrowing-141810-2.stderr +++ b/tests/ui/typeck/consider-borrowing-141810-2.stderr @@ -1,15 +1,19 @@ error[E0308]: `if` and `else` have incompatible types - --> $DIR/consider-borrowing-141810-2.rs:4:12 + --> $DIR/consider-borrowing-141810-2.rs:4:21 | LL | let x = if true { | ------- `if` and `else` have incompatible types LL | &() | --- expected because of this LL | } else if false { - | ____________^ + | _____________________^ LL | | } else { -LL | | }; | |_____^ expected `&()`, found `()` + | +help: consider borrowing here + | +LL | } else if false &{ + | + error: aborting due to 1 previous error diff --git a/tests/ui/typeck/consider-borrowing-141810-3.stderr b/tests/ui/typeck/consider-borrowing-141810-3.stderr index 3adf8ba1a8924..df43c27ebfc3d 100644 --- a/tests/ui/typeck/consider-borrowing-141810-3.stderr +++ b/tests/ui/typeck/consider-borrowing-141810-3.stderr @@ -1,18 +1,20 @@ error[E0308]: `if` and `else` have incompatible types - --> $DIR/consider-borrowing-141810-3.rs:4:12 + --> $DIR/consider-borrowing-141810-3.rs:4:21 | LL | let x = if true { | ------- `if` and `else` have incompatible types LL | &() | --- expected because of this LL | } else if false { - | ____________^ + | _____________________^ LL | | LL | | }; | |_____^ expected `&()`, found `()` | - = note: `if` expressions without `else` evaluate to `()` - = note: consider adding an `else` block that evaluates to the expected type +help: consider borrowing here + | +LL | } else if false &{ + | + error: aborting due to 1 previous error