Skip to content

Commit 92bebac

Browse files
authoredJul 22, 2022
Rollup merge of #99539 - compiler-errors:bidirectional-block-suggestions, r=fee1-dead
Improve suggestions for returning binding Fixes #99525 Also reworks the cause codes for match and if a bit, I think cleaning them up in a positive way. We no longer need to call `could_remove_semicolon` in successful code, which might save a few cycles?
2 parents aec458b + 3eef023 commit 92bebac

File tree

13 files changed

+625
-467
lines changed

13 files changed

+625
-467
lines changed
 

‎compiler/rustc_hir/src/hir.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,16 @@ pub struct Block<'hir> {
954954
pub targeted_by_break: bool,
955955
}
956956

957+
impl<'hir> Block<'hir> {
958+
pub fn innermost_block(&self) -> &Block<'hir> {
959+
let mut block = self;
960+
while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr {
961+
block = inner_block;
962+
}
963+
block
964+
}
965+
}
966+
957967
#[derive(Debug, HashStable_Generic)]
958968
pub struct Pat<'hir> {
959969
#[stable_hasher(ignore)]

‎compiler/rustc_infer/src/infer/error_reporting/mod.rs

Lines changed: 422 additions & 117 deletions
Large diffs are not rendered by default.

‎compiler/rustc_middle/src/traits/mod.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ pub enum ObligationCauseCode<'tcx> {
351351
ConstPatternStructural,
352352

353353
/// Computing common supertype in an if expression
354-
IfExpression(Box<IfExpressionCause>),
354+
IfExpression(Box<IfExpressionCause<'tcx>>),
355355

356356
/// Computing common supertype of an if expression with no else counter-part
357357
IfExpressionWithNoElse,
@@ -488,22 +488,27 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression {
488488

489489
#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
490490
pub struct MatchExpressionArmCause<'tcx> {
491+
pub arm_block_id: Option<hir::HirId>,
492+
pub arm_ty: Ty<'tcx>,
491493
pub arm_span: Span,
494+
pub prior_arm_block_id: Option<hir::HirId>,
495+
pub prior_arm_ty: Ty<'tcx>,
496+
pub prior_arm_span: Span,
492497
pub scrut_span: Span,
493-
pub semi_span: Option<(Span, StatementAsExpression)>,
494498
pub source: hir::MatchSource,
495499
pub prior_arms: Vec<Span>,
496-
pub last_ty: Ty<'tcx>,
497500
pub scrut_hir_id: hir::HirId,
498501
pub opt_suggest_box_span: Option<Span>,
499502
}
500503

501-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
502-
pub struct IfExpressionCause {
503-
pub then: Span,
504-
pub else_sp: Span,
505-
pub outer: Option<Span>,
506-
pub semicolon: Option<(Span, StatementAsExpression)>,
504+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
505+
#[derive(Lift, TypeFoldable, TypeVisitable)]
506+
pub struct IfExpressionCause<'tcx> {
507+
pub then_id: hir::HirId,
508+
pub else_id: hir::HirId,
509+
pub then_ty: Ty<'tcx>,
510+
pub else_ty: Ty<'tcx>,
511+
pub outer_span: Option<Span>,
507512
pub opt_suggest_box_span: Option<Span>,
508513
}
509514

‎compiler/rustc_middle/src/traits/structural_impls.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
130130
// Lift implementations
131131

132132
TrivialTypeTraversalAndLiftImpls! {
133-
super::IfExpressionCause,
134133
super::ImplSourceDiscriminantKindData,
135134
super::ImplSourcePointeeData,
136135
}

‎compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use rustc_hir::intravisit::Visitor;
2222
use rustc_hir::GenericParam;
2323
use rustc_hir::Item;
2424
use rustc_hir::Node;
25-
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
2625
use rustc_infer::traits::TraitEngine;
2726
use rustc_middle::traits::select::OverflowError;
2827
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -640,7 +639,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
640639
if expected.len() == 1 { "" } else { "s" },
641640
)
642641
);
643-
} else if !same_type_modulo_infer(given_ty, expected_ty) {
642+
} else if !self.same_type_modulo_infer(given_ty, expected_ty) {
644643
// Print type mismatch
645644
let (expected_args, given_args) =
646645
self.cmp(given_ty, expected_ty);

‎compiler/rustc_typeck/src/check/_match.rs

Lines changed: 68 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use rustc_span::Span;
99
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1010
use rustc_trait_selection::traits::{
1111
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
12-
StatementAsExpression,
1312
};
1413

1514
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -75,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7574
};
7675

7776
let mut other_arms = vec![]; // Used only for diagnostics.
78-
let mut prior_arm_ty = None;
79-
for (i, arm) in arms.iter().enumerate() {
77+
let mut prior_arm = None;
78+
for arm in arms {
8079
if let Some(g) = &arm.guard {
8180
self.diverges.set(Diverges::Maybe);
8281
match g {
@@ -96,21 +95,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9695

9796
let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
9897

99-
let (arm_span, semi_span) =
100-
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
101-
let (span, code) = match i {
98+
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
99+
(Some(blk.hir_id), self.find_block_span(blk))
100+
} else {
101+
(None, arm.body.span)
102+
};
103+
104+
let (span, code) = match prior_arm {
102105
// The reason for the first arm to fail is not that the match arms diverge,
103106
// but rather that there's a prior obligation that doesn't hold.
104-
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
105-
_ => (
107+
None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
108+
Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => (
106109
expr.span,
107110
ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause {
111+
arm_block_id,
108112
arm_span,
113+
arm_ty,
114+
prior_arm_block_id,
115+
prior_arm_ty,
116+
prior_arm_span,
109117
scrut_span: scrut.span,
110-
semi_span,
111118
source: match_src,
112119
prior_arms: other_arms.clone(),
113-
last_ty: prior_arm_ty.unwrap(),
114120
scrut_hir_id: scrut.hir_id,
115121
opt_suggest_box_span,
116122
})),
@@ -139,7 +145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
139145
let ret_ty = ret_coercion.borrow().expected_ty();
140146
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
141147
self.can_coerce(arm_ty, ret_ty)
142-
&& prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
148+
&& prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
143149
// The match arms need to unify for the case of `impl Trait`.
144150
&& !matches!(ret_ty.kind(), ty::Opaque(..))
145151
}
@@ -181,7 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
181187
if other_arms.len() > 5 {
182188
other_arms.remove(0);
183189
}
184-
prior_arm_ty = Some(arm_ty);
190+
191+
prior_arm = Some((arm_block_id, arm_ty, arm_span));
185192
}
186193

187194
// If all of the arms in the `match` diverge,
@@ -207,28 +214,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
207214
match_ty
208215
}
209216

210-
fn get_appropriate_arm_semicolon_removal_span(
211-
&self,
212-
arms: &'tcx [hir::Arm<'tcx>],
213-
i: usize,
214-
prior_arm_ty: Option<Ty<'tcx>>,
215-
arm_ty: Ty<'tcx>,
216-
) -> (Span, Option<(Span, StatementAsExpression)>) {
217-
let arm = &arms[i];
218-
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
219-
self.find_block_span(blk, prior_arm_ty)
220-
} else {
221-
(arm.body.span, None)
222-
};
223-
if semi_span.is_none() && i > 0 {
224-
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
225-
let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
226-
semi_span = semi_span_prev;
227-
}
228-
}
229-
(arm_span, semi_span)
230-
}
231-
232217
/// When the previously checked expression (the scrutinee) diverges,
233218
/// warn the user about the match arms being unreachable.
234219
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
@@ -313,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
313298
else_ty: Ty<'tcx>,
314299
opt_suggest_box_span: Option<Span>,
315300
) -> ObligationCause<'tcx> {
316-
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
301+
let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
317302
// The `if`/`else` isn't in one line in the output, include some context to make it
318303
// clear it is an if/else expression:
319304
// ```
@@ -339,69 +324,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
339324
None
340325
};
341326

342-
let mut remove_semicolon = None;
343-
let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
344-
let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty));
345-
remove_semicolon = semi_sp;
346-
if block.expr.is_none() && block.stmts.is_empty() {
347-
// Avoid overlapping spans that aren't as readable:
348-
// ```
349-
// 2 | let x = if true {
350-
// | _____________-
351-
// 3 | | 3
352-
// | | - expected because of this
353-
// 4 | | } else {
354-
// | |____________^
355-
// 5 | ||
356-
// 6 | || };
357-
// | || ^
358-
// | ||_____|
359-
// | |______if and else have incompatible types
360-
// | expected integer, found `()`
361-
// ```
362-
// by not pointing at the entire expression:
363-
// ```
364-
// 2 | let x = if true {
365-
// | ------- `if` and `else` have incompatible types
366-
// 3 | 3
367-
// | - expected because of this
368-
// 4 | } else {
369-
// | ____________^
370-
// 5 | |
371-
// 6 | | };
372-
// | |_____^ expected integer, found `()`
373-
// ```
374-
if outer_sp.is_some() {
375-
outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
376-
}
327+
let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
328+
let block = block.innermost_block();
329+
330+
// Avoid overlapping spans that aren't as readable:
331+
// ```
332+
// 2 | let x = if true {
333+
// | _____________-
334+
// 3 | | 3
335+
// | | - expected because of this
336+
// 4 | | } else {
337+
// | |____________^
338+
// 5 | ||
339+
// 6 | || };
340+
// | || ^
341+
// | ||_____|
342+
// | |______if and else have incompatible types
343+
// | expected integer, found `()`
344+
// ```
345+
// by not pointing at the entire expression:
346+
// ```
347+
// 2 | let x = if true {
348+
// | ------- `if` and `else` have incompatible types
349+
// 3 | 3
350+
// | - expected because of this
351+
// 4 | } else {
352+
// | ____________^
353+
// 5 | |
354+
// 6 | | };
355+
// | |_____^ expected integer, found `()`
356+
// ```
357+
if block.expr.is_none() && block.stmts.is_empty()
358+
&& let Some(outer_span) = &mut outer_span
359+
{
360+
*outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span);
377361
}
378-
error_sp
362+
363+
(self.find_block_span(block), block.hir_id)
379364
} else {
380-
// shouldn't happen unless the parser has done something weird
381-
else_expr.span
365+
(else_expr.span, else_expr.hir_id)
382366
};
383367

384-
// Compute `Span` of `then` part of `if`-expression.
385-
let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
386-
let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty));
387-
remove_semicolon = remove_semicolon.or(semi_sp);
368+
let then_id = if let ExprKind::Block(block, _) = &then_expr.kind {
369+
let block = block.innermost_block();
370+
// Exclude overlapping spans
388371
if block.expr.is_none() && block.stmts.is_empty() {
389-
outer_sp = None; // same as in `error_sp`; cleanup output
372+
outer_span = None;
390373
}
391-
then_sp
374+
block.hir_id
392375
} else {
393-
// shouldn't happen unless the parser has done something weird
394-
then_expr.span
376+
then_expr.hir_id
395377
};
396378

397379
// Finally construct the cause:
398380
self.cause(
399381
error_sp,
400382
ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
401-
then: then_sp,
402-
else_sp: error_sp,
403-
outer: outer_sp,
404-
semicolon: remove_semicolon,
383+
else_id,
384+
then_id,
385+
then_ty,
386+
else_ty,
387+
outer_span,
405388
opt_suggest_box_span,
406389
})),
407390
)
@@ -482,22 +465,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
482465
}
483466
}
484467

485-
fn find_block_span(
486-
&self,
487-
block: &'tcx hir::Block<'tcx>,
488-
expected_ty: Option<Ty<'tcx>>,
489-
) -> (Span, Option<(Span, StatementAsExpression)>) {
490-
if let Some(expr) = &block.expr {
491-
(expr.span, None)
492-
} else if let Some(stmt) = block.stmts.last() {
493-
// possibly incorrect trailing `;` in the else arm
494-
(stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty)))
495-
} else {
496-
// empty block; point at its entirety
497-
(block.span, None)
498-
}
499-
}
500-
501468
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
502469
// we check if the different arms would work with boxed trait objects instead and
503470
// provide a structured suggestion in that case.

‎compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs

Lines changed: 2 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,15 @@ use rustc_middle::ty::{
3030
};
3131
use rustc_session::lint;
3232
use rustc_span::hygiene::DesugaringKind;
33-
use rustc_span::source_map::{original_sp, DUMMY_SP};
3433
use rustc_span::symbol::{kw, sym, Ident};
35-
use rustc_span::{self, BytePos, Span};
34+
use rustc_span::{Span, DUMMY_SP};
3635
use rustc_trait_selection::infer::InferCtxtExt as _;
3736
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
3837
use rustc_trait_selection::traits::{
39-
self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
38+
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
4039
};
4140

4241
use std::collections::hash_map::Entry;
43-
use std::iter;
4442
use std::slice;
4543

4644
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -1059,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10591057
));
10601058
}
10611059

1062-
pub(in super::super) fn could_remove_semicolon(
1063-
&self,
1064-
blk: &'tcx hir::Block<'tcx>,
1065-
expected_ty: Ty<'tcx>,
1066-
) -> Option<(Span, StatementAsExpression)> {
1067-
// Be helpful when the user wrote `{... expr;}` and
1068-
// taking the `;` off is enough to fix the error.
1069-
let last_stmt = blk.stmts.last()?;
1070-
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
1071-
return None;
1072-
};
1073-
let last_expr_ty = self.node_ty(last_expr.hir_id);
1074-
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
1075-
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
1076-
if last_def_id == exp_def_id =>
1077-
{
1078-
StatementAsExpression::CorrectType
1079-
}
1080-
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
1081-
debug!(
1082-
"both opaque, likely future {:?} {:?} {:?} {:?}",
1083-
last_def_id, last_bounds, exp_def_id, exp_bounds
1084-
);
1085-
1086-
let last_local_id = last_def_id.as_local()?;
1087-
let exp_local_id = exp_def_id.as_local()?;
1088-
1089-
match (
1090-
&self.tcx.hir().expect_item(last_local_id).kind,
1091-
&self.tcx.hir().expect_item(exp_local_id).kind,
1092-
) {
1093-
(
1094-
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
1095-
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
1096-
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
1097-
match (left, right) {
1098-
(
1099-
hir::GenericBound::Trait(tl, ml),
1100-
hir::GenericBound::Trait(tr, mr),
1101-
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
1102-
&& ml == mr =>
1103-
{
1104-
true
1105-
}
1106-
(
1107-
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
1108-
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
1109-
) if langl == langr => {
1110-
// FIXME: consider the bounds!
1111-
debug!("{:?} {:?}", argsl, argsr);
1112-
true
1113-
}
1114-
_ => false,
1115-
}
1116-
}) =>
1117-
{
1118-
StatementAsExpression::NeedsBoxing
1119-
}
1120-
_ => StatementAsExpression::CorrectType,
1121-
}
1122-
}
1123-
_ => StatementAsExpression::CorrectType,
1124-
};
1125-
if (matches!(last_expr_ty.kind(), ty::Error(_))
1126-
|| self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
1127-
&& matches!(needs_box, StatementAsExpression::CorrectType)
1128-
{
1129-
return None;
1130-
}
1131-
let span = if last_stmt.span.from_expansion() {
1132-
let mac_call = original_sp(last_stmt.span, blk.span);
1133-
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
1134-
} else {
1135-
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
1136-
};
1137-
Some((span, needs_box))
1138-
}
1139-
11401060
// Instantiates the given path, which must refer to an item with the given
11411061
// number of type parameters and type.
11421062
#[instrument(skip(self, span), level = "debug")]

‎compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

Lines changed: 1 addition & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use crate::astconv::AstConv;
33
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
44

55
use rustc_ast::util::parser::ExprPrecedence;
6-
use rustc_data_structures::fx::FxHashSet;
76
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
87
use rustc_hir as hir;
98
use rustc_hir::def::{CtorOf, DefKind};
@@ -14,7 +13,7 @@ use rustc_hir::{
1413
use rustc_infer::infer::{self, TyCtxtInferExt};
1514
use rustc_infer::traits::{self, StatementAsExpression};
1615
use rustc_middle::lint::in_external_macro;
17-
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty, TypeVisitable};
16+
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
1817
use rustc_span::symbol::sym;
1918
use rustc_span::Span;
2019
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
@@ -904,117 +903,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
904903
false
905904
}
906905
}
907-
908-
pub(crate) fn consider_returning_binding(
909-
&self,
910-
blk: &'tcx hir::Block<'tcx>,
911-
expected_ty: Ty<'tcx>,
912-
err: &mut Diagnostic,
913-
) {
914-
let mut shadowed = FxHashSet::default();
915-
let mut candidate_idents = vec![];
916-
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
917-
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
918-
&& let Some(pat_ty) = self.typeck_results.borrow().node_type_opt(*hir_id)
919-
{
920-
let pat_ty = self.resolve_vars_if_possible(pat_ty);
921-
if self.can_coerce(pat_ty, expected_ty)
922-
&& !(pat_ty, expected_ty).references_error()
923-
&& shadowed.insert(ident.name)
924-
{
925-
candidate_idents.push((*ident, pat_ty));
926-
}
927-
}
928-
true
929-
};
930-
931-
let hir = self.tcx.hir();
932-
for stmt in blk.stmts.iter().rev() {
933-
let StmtKind::Local(local) = &stmt.kind else { continue; };
934-
local.pat.walk(&mut find_compatible_candidates);
935-
}
936-
match hir.find(hir.get_parent_node(blk.hir_id)) {
937-
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
938-
match hir.find(hir.get_parent_node(*hir_id)) {
939-
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
940-
pat.walk(&mut find_compatible_candidates);
941-
}
942-
Some(
943-
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
944-
| hir::Node::ImplItem(hir::ImplItem {
945-
kind: hir::ImplItemKind::Fn(_, body),
946-
..
947-
})
948-
| hir::Node::TraitItem(hir::TraitItem {
949-
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
950-
..
951-
})
952-
| hir::Node::Expr(hir::Expr {
953-
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
954-
..
955-
}),
956-
) => {
957-
for param in hir.body(*body).params {
958-
param.pat.walk(&mut find_compatible_candidates);
959-
}
960-
}
961-
Some(hir::Node::Expr(hir::Expr {
962-
kind:
963-
hir::ExprKind::If(
964-
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
965-
then_block,
966-
_,
967-
),
968-
..
969-
})) if then_block.hir_id == *hir_id => {
970-
let_.pat.walk(&mut find_compatible_candidates);
971-
}
972-
_ => {}
973-
}
974-
}
975-
_ => {}
976-
}
977-
978-
match &candidate_idents[..] {
979-
[(ident, _ty)] => {
980-
let sm = self.tcx.sess.source_map();
981-
if let Some(stmt) = blk.stmts.last() {
982-
let stmt_span = sm.stmt_span(stmt.span, blk.span);
983-
let sugg = if sm.is_multiline(blk.span)
984-
&& let Some(spacing) = sm.indentation_before(stmt_span)
985-
{
986-
format!("\n{spacing}{ident}")
987-
} else {
988-
format!(" {ident}")
989-
};
990-
err.span_suggestion_verbose(
991-
stmt_span.shrink_to_hi(),
992-
format!("consider returning the local binding `{ident}`"),
993-
sugg,
994-
Applicability::MachineApplicable,
995-
);
996-
} else {
997-
let sugg = if sm.is_multiline(blk.span)
998-
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
999-
{
1000-
format!("\n{spacing} {ident}\n{spacing}")
1001-
} else {
1002-
format!(" {ident} ")
1003-
};
1004-
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
1005-
err.span_suggestion_verbose(
1006-
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
1007-
format!("consider returning the local binding `{ident}`"),
1008-
sugg,
1009-
Applicability::MachineApplicable,
1010-
);
1011-
}
1012-
}
1013-
values if (1..3).contains(&values.len()) => {
1014-
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
1015-
err.span_note(spans, "consider returning one of these bindings");
1016-
}
1017-
_ => {}
1018-
}
1019-
}
1020906
}

‎src/test/ui/async-await/async-block-control-flow-static-semantics.stderr

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,6 @@ LL | | break 0u8;
1818
LL | | };
1919
| |_________- enclosing `async` block
2020

21-
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
22-
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
23-
|
24-
LL | let _: &dyn Future<Output = ()> = &block;
25-
| ^^^^^^ expected `()`, found `u8`
26-
|
27-
= note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
28-
2921
error[E0308]: mismatched types
3022
--> $DIR/async-block-control-flow-static-semantics.rs:21:58
3123
|
@@ -40,7 +32,7 @@ LL | | }
4032
| |_^ expected `u8`, found `()`
4133

4234
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
43-
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
35+
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
4436
|
4537
LL | let _: &dyn Future<Output = ()> = &block;
4638
| ^^^^^^ expected `()`, found `u8`
@@ -55,6 +47,14 @@ LL | fn return_targets_async_block_not_fn() -> u8 {
5547
| |
5648
| implicitly returns `()` as its body has no tail or `return` expression
5749

50+
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
51+
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
52+
|
53+
LL | let _: &dyn Future<Output = ()> = &block;
54+
| ^^^^^^ expected `()`, found `u8`
55+
|
56+
= note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
57+
5858
error[E0308]: mismatched types
5959
--> $DIR/async-block-control-flow-static-semantics.rs:47:44
6060
|

‎src/test/ui/issues/issue-59494.stderr

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ LL | let t8 = t8n(t7, t7p(f, g));
77
| required by a bound introduced by this call
88
|
99
= help: the trait `Fn<(_,)>` is not implemented for `impl Fn(((_, _), _))`
10-
= note: expected a closure with arguments `(((_, _), _),)`
11-
found a closure with arguments `(_,)`
1210
note: required by a bound in `t8n`
1311
--> $DIR/issue-59494.rs:5:45
1412
|

‎src/test/ui/suggestions/return-bindings.fixed

Lines changed: 0 additions & 23 deletions
This file was deleted.

‎src/test/ui/suggestions/return-bindings.rs

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// run-rustfix
2-
31
#![allow(unused)]
42

53
fn a(i: i32) -> i32 {}
@@ -18,4 +16,36 @@ fn c() -> Option<i32> {
1816
let x = Some(1);
1917
}
2018

19+
fn d(opt_str: Option<String>) {
20+
let s: String = if let Some(s) = opt_str {
21+
//~^ ERROR mismatched types
22+
} else {
23+
String::new()
24+
};
25+
}
26+
27+
fn d2(opt_str: Option<String>) {
28+
let s = if let Some(s) = opt_str {
29+
} else {
30+
String::new()
31+
//~^ ERROR `if` and `else` have incompatible types
32+
};
33+
}
34+
35+
fn e(opt_str: Option<String>) {
36+
let s: String = match opt_str {
37+
Some(s) => {}
38+
//~^ ERROR mismatched types
39+
None => String::new(),
40+
};
41+
}
42+
43+
fn e2(opt_str: Option<String>) {
44+
let s = match opt_str {
45+
Some(s) => {}
46+
None => String::new(),
47+
//~^ ERROR `match` arms have incompatible types
48+
};
49+
}
50+
2151
fn main() {}

‎src/test/ui/suggestions/return-bindings.stderr

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/return-bindings.rs:5:17
2+
--> $DIR/return-bindings.rs:3:17
33
|
44
LL | fn a(i: i32) -> i32 {}
55
| - ^^^ expected `i32`, found `()`
@@ -12,7 +12,7 @@ LL | fn a(i: i32) -> i32 { i }
1212
| +
1313

1414
error[E0308]: mismatched types
15-
--> $DIR/return-bindings.rs:9:46
15+
--> $DIR/return-bindings.rs:7:46
1616
|
1717
LL | let s: String = if let Some(s) = opt_str {
1818
| ______________________________________________^
@@ -28,7 +28,7 @@ LL ~
2828
|
2929

3030
error[E0308]: mismatched types
31-
--> $DIR/return-bindings.rs:16:11
31+
--> $DIR/return-bindings.rs:14:11
3232
|
3333
LL | fn c() -> Option<i32> {
3434
| - ^^^^^^^^^^^ expected enum `Option`, found `()`
@@ -43,6 +43,68 @@ LL ~ let x = Some(1);
4343
LL + x
4444
|
4545

46-
error: aborting due to 3 previous errors
46+
error[E0308]: mismatched types
47+
--> $DIR/return-bindings.rs:20:46
48+
|
49+
LL | let s: String = if let Some(s) = opt_str {
50+
| ______________________________________________^
51+
LL | |
52+
LL | | } else {
53+
| |_____^ expected struct `String`, found `()`
54+
|
55+
help: consider returning the local binding `s`
56+
|
57+
LL ~ let s: String = if let Some(s) = opt_str {
58+
LL + s
59+
LL ~
60+
|
61+
62+
error[E0308]: `if` and `else` have incompatible types
63+
--> $DIR/return-bindings.rs:30:9
64+
|
65+
LL | let s = if let Some(s) = opt_str {
66+
| ______________________________________-
67+
LL | | } else {
68+
| |_____- expected because of this
69+
LL | String::new()
70+
| ^^^^^^^^^^^^^ expected `()`, found struct `String`
71+
|
72+
help: consider returning the local binding `s`
73+
|
74+
LL ~ let s = if let Some(s) = opt_str {
75+
LL + s
76+
LL ~ } else {
77+
|
78+
79+
error[E0308]: mismatched types
80+
--> $DIR/return-bindings.rs:37:20
81+
|
82+
LL | Some(s) => {}
83+
| ^^ expected struct `String`, found `()`
84+
|
85+
help: consider returning the local binding `s`
86+
|
87+
LL | Some(s) => { s }
88+
| +
89+
90+
error[E0308]: `match` arms have incompatible types
91+
--> $DIR/return-bindings.rs:46:17
92+
|
93+
LL | let s = match opt_str {
94+
| _____________-
95+
LL | | Some(s) => {}
96+
| | -- this is found to be of type `()`
97+
LL | | None => String::new(),
98+
| | ^^^^^^^^^^^^^ expected `()`, found struct `String`
99+
LL | |
100+
LL | | };
101+
| |_____- `match` arms have incompatible types
102+
|
103+
help: consider returning the local binding `s`
104+
|
105+
LL | Some(s) => { s }
106+
| +
107+
108+
error: aborting due to 7 previous errors
47109

48110
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)
Please sign in to comment.