Do if-expression obligation stuff less eagerly
This commit is contained in:
parent
3d9dd681f5
commit
99c32570bb
@ -954,6 +954,16 @@ pub struct Block<'hir> {
|
|||||||
pub targeted_by_break: bool,
|
pub targeted_by_break: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'hir> Block<'hir> {
|
||||||
|
pub fn peel_blocks(&self) -> &Block<'hir> {
|
||||||
|
let mut block = self;
|
||||||
|
while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr {
|
||||||
|
block = inner_block;
|
||||||
|
}
|
||||||
|
block
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, HashStable_Generic)]
|
#[derive(Debug, HashStable_Generic)]
|
||||||
pub struct Pat<'hir> {
|
pub struct Pat<'hir> {
|
||||||
#[stable_hasher(ignore)]
|
#[stable_hasher(ignore)]
|
||||||
|
@ -721,25 +721,39 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
ObligationCauseCode::IfExpression(box IfExpressionCause {
|
ObligationCauseCode::IfExpression(box IfExpressionCause {
|
||||||
then,
|
then_id,
|
||||||
else_sp,
|
else_id,
|
||||||
outer,
|
then_ty,
|
||||||
semicolon,
|
else_ty,
|
||||||
|
outer_span,
|
||||||
opt_suggest_box_span,
|
opt_suggest_box_span,
|
||||||
}) => {
|
}) => {
|
||||||
err.span_label(then, "expected because of this");
|
let then_span = self.find_block_span_from_hir_id(then_id);
|
||||||
if let Some(sp) = outer {
|
let else_span = self.find_block_span_from_hir_id(then_id);
|
||||||
|
err.span_label(then_span, "expected because of this");
|
||||||
|
if let Some(sp) = outer_span {
|
||||||
err.span_label(sp, "`if` and `else` have incompatible types");
|
err.span_label(sp, "`if` and `else` have incompatible types");
|
||||||
}
|
}
|
||||||
|
let semicolon = if let hir::Node::Block(blk) = self.tcx.hir().get(then_id)
|
||||||
|
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, else_ty)
|
||||||
|
{
|
||||||
|
Some(remove_semicolon)
|
||||||
|
} else if let hir::Node::Block(blk) = self.tcx.hir().get(else_id)
|
||||||
|
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, then_ty)
|
||||||
|
{
|
||||||
|
Some(remove_semicolon)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
if let Some((sp, boxed)) = semicolon {
|
if let Some((sp, boxed)) = semicolon {
|
||||||
if matches!(boxed, StatementAsExpression::NeedsBoxing) {
|
if matches!(boxed, StatementAsExpression::NeedsBoxing) {
|
||||||
err.multipart_suggestion(
|
err.multipart_suggestion(
|
||||||
"consider removing this semicolon and boxing the expression",
|
"consider removing this semicolon and boxing the expression",
|
||||||
vec![
|
vec![
|
||||||
(then.shrink_to_lo(), "Box::new(".to_string()),
|
(then_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
(then.shrink_to_hi(), ")".to_string()),
|
(then_span.shrink_to_hi(), ")".to_string()),
|
||||||
(else_sp.shrink_to_lo(), "Box::new(".to_string()),
|
(else_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||||
(else_sp.shrink_to_hi(), ")".to_string()),
|
(else_span.shrink_to_hi(), ")".to_string()),
|
||||||
(sp, String::new()),
|
(sp, String::new()),
|
||||||
],
|
],
|
||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
@ -752,12 +766,21 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
Applicability::MachineApplicable,
|
Applicability::MachineApplicable,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
let suggested = if let hir::Node::Block(blk) = self.tcx.hir().get(then_id) {
|
||||||
|
self.consider_returning_binding(blk, else_ty, err)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
if !suggested && let hir::Node::Block(blk) = self.tcx.hir().get(else_id) {
|
||||||
|
self.consider_returning_binding(blk, then_ty, err);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if let Some(ret_sp) = opt_suggest_box_span {
|
if let Some(ret_sp) = opt_suggest_box_span {
|
||||||
self.suggest_boxing_for_return_impl_trait(
|
self.suggest_boxing_for_return_impl_trait(
|
||||||
err,
|
err,
|
||||||
ret_sp,
|
ret_sp,
|
||||||
[then, else_sp].into_iter(),
|
[then_span, else_span].into_iter(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1870,40 +1893,41 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder),
|
self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder),
|
||||||
self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder),
|
self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder),
|
||||||
) {
|
) {
|
||||||
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => {
|
(Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause
|
||||||
match cause.code() {
|
.code()
|
||||||
ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => {
|
{
|
||||||
|
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||||
|
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||||
|
diag.multipart_suggestion(
|
||||||
|
"consider `await`ing on both `Future`s",
|
||||||
|
vec![
|
||||||
|
(then_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||||
|
],
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||||
|
prior_arms,
|
||||||
|
..
|
||||||
|
}) => {
|
||||||
|
if let [.., arm_span] = &prior_arms[..] {
|
||||||
diag.multipart_suggestion(
|
diag.multipart_suggestion(
|
||||||
"consider `await`ing on both `Future`s",
|
"consider `await`ing on both `Future`s",
|
||||||
vec![
|
vec![
|
||||||
(then.shrink_to_hi(), ".await".to_string()),
|
(arm_span.shrink_to_hi(), ".await".to_string()),
|
||||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
(exp_span.shrink_to_hi(), ".await".to_string()),
|
||||||
],
|
],
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
} else {
|
||||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
|
||||||
prior_arms,
|
|
||||||
..
|
|
||||||
}) => {
|
|
||||||
if let [.., arm_span] = &prior_arms[..] {
|
|
||||||
diag.multipart_suggestion(
|
|
||||||
"consider `await`ing on both `Future`s",
|
|
||||||
vec![
|
|
||||||
(arm_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
(exp_span.shrink_to_hi(), ".await".to_string()),
|
|
||||||
],
|
|
||||||
Applicability::MaybeIncorrect,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
diag.help("consider `await`ing on both `Future`s");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
diag.help("consider `await`ing on both `Future`s");
|
diag.help("consider `await`ing on both `Future`s");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
_ => {
|
||||||
|
diag.help("consider `await`ing on both `Future`s");
|
||||||
|
}
|
||||||
|
},
|
||||||
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
(_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => {
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
exp_span.shrink_to_hi(),
|
exp_span.shrink_to_hi(),
|
||||||
@ -1914,10 +1938,18 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
(Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code()
|
||||||
{
|
{
|
||||||
ObligationCauseCode::Pattern { span: Some(span), .. }
|
ObligationCauseCode::Pattern { span: Some(then_span), .. } => {
|
||||||
| ObligationCauseCode::IfExpression(box IfExpressionCause { then: span, .. }) => {
|
|
||||||
diag.span_suggestion_verbose(
|
diag.span_suggestion_verbose(
|
||||||
span.shrink_to_hi(),
|
then_span.shrink_to_hi(),
|
||||||
|
"consider `await`ing on the `Future`",
|
||||||
|
".await",
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => {
|
||||||
|
let then_span = self.find_block_span_from_hir_id(*then_id);
|
||||||
|
diag.span_suggestion_verbose(
|
||||||
|
then_span.shrink_to_hi(),
|
||||||
"consider `await`ing on the `Future`",
|
"consider `await`ing on the `Future`",
|
||||||
".await",
|
".await",
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
@ -2808,3 +2840,230 @@ impl TyCategory {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> InferCtxt<'_, 'tcx> {
|
||||||
|
pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span {
|
||||||
|
let block = block.peel_blocks();
|
||||||
|
if let Some(expr) = &block.expr {
|
||||||
|
expr.span
|
||||||
|
} else if let Some(stmt) = block.stmts.last() {
|
||||||
|
// possibly incorrect trailing `;` in the else arm
|
||||||
|
stmt.span
|
||||||
|
} else {
|
||||||
|
// empty block; point at its entirety
|
||||||
|
block.span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span {
|
||||||
|
match self.tcx.hir().get(hir_id) {
|
||||||
|
hir::Node::Block(blk) => self.find_block_span(blk),
|
||||||
|
// The parser was in a weird state if either of these happen...
|
||||||
|
hir::Node::Expr(e) => e.span,
|
||||||
|
_ => rustc_span::DUMMY_SP,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn could_remove_semicolon(
|
||||||
|
&self,
|
||||||
|
blk: &'tcx hir::Block<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
) -> Option<(Span, StatementAsExpression)> {
|
||||||
|
let blk = blk.peel_blocks();
|
||||||
|
// Do not suggest if we have a tail expr.
|
||||||
|
if blk.expr.is_some() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
// Be helpful when the user wrote `{... expr;}` and
|
||||||
|
// taking the `;` off is enough to fix the error.
|
||||||
|
let last_stmt = blk.stmts.last()?;
|
||||||
|
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
||||||
|
return None;
|
||||||
|
};
|
||||||
|
let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?;
|
||||||
|
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
||||||
|
_ if last_expr_ty.references_error() => return None,
|
||||||
|
_ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => {
|
||||||
|
StatementAsExpression::CorrectType
|
||||||
|
}
|
||||||
|
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
||||||
|
if last_def_id == exp_def_id =>
|
||||||
|
{
|
||||||
|
StatementAsExpression::CorrectType
|
||||||
|
}
|
||||||
|
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
||||||
|
debug!(
|
||||||
|
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
||||||
|
last_def_id, last_bounds, exp_def_id, exp_bounds
|
||||||
|
);
|
||||||
|
|
||||||
|
let last_local_id = last_def_id.as_local()?;
|
||||||
|
let exp_local_id = exp_def_id.as_local()?;
|
||||||
|
|
||||||
|
match (
|
||||||
|
&self.tcx.hir().expect_item(last_local_id).kind,
|
||||||
|
&self.tcx.hir().expect_item(exp_local_id).kind,
|
||||||
|
) {
|
||||||
|
(
|
||||||
|
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
||||||
|
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
||||||
|
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
||||||
|
match (left, right) {
|
||||||
|
(
|
||||||
|
hir::GenericBound::Trait(tl, ml),
|
||||||
|
hir::GenericBound::Trait(tr, mr),
|
||||||
|
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
||||||
|
&& ml == mr =>
|
||||||
|
{
|
||||||
|
true
|
||||||
|
}
|
||||||
|
(
|
||||||
|
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
||||||
|
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
||||||
|
) if langl == langr => {
|
||||||
|
// FIXME: consider the bounds!
|
||||||
|
debug!("{:?} {:?}", argsl, argsr);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}) =>
|
||||||
|
{
|
||||||
|
StatementAsExpression::NeedsBoxing
|
||||||
|
}
|
||||||
|
_ => StatementAsExpression::CorrectType,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let span = if last_stmt.span.from_expansion() {
|
||||||
|
let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span);
|
||||||
|
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
||||||
|
} else {
|
||||||
|
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
||||||
|
};
|
||||||
|
Some((span, needs_box))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn consider_returning_binding(
|
||||||
|
&self,
|
||||||
|
blk: &'tcx hir::Block<'tcx>,
|
||||||
|
expected_ty: Ty<'tcx>,
|
||||||
|
err: &mut Diagnostic,
|
||||||
|
) -> bool {
|
||||||
|
let blk = blk.peel_blocks();
|
||||||
|
// Do not suggest if we have a tail expr.
|
||||||
|
if blk.expr.is_some() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let mut shadowed = FxHashSet::default();
|
||||||
|
let mut candidate_idents = vec![];
|
||||||
|
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
||||||
|
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
||||||
|
&& let Some(pat_ty) = self
|
||||||
|
.in_progress_typeck_results
|
||||||
|
.and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id))
|
||||||
|
{
|
||||||
|
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
||||||
|
if self.same_type_modulo_infer(pat_ty, expected_ty)
|
||||||
|
&& !(pat_ty, expected_ty).references_error()
|
||||||
|
&& shadowed.insert(ident.name)
|
||||||
|
{
|
||||||
|
candidate_idents.push((*ident, pat_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
};
|
||||||
|
|
||||||
|
let hir = self.tcx.hir();
|
||||||
|
for stmt in blk.stmts.iter().rev() {
|
||||||
|
let hir::StmtKind::Local(local) = &stmt.kind else { continue; };
|
||||||
|
local.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
||||||
|
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
||||||
|
match hir.find(hir.get_parent_node(*hir_id)) {
|
||||||
|
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
||||||
|
pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
Some(
|
||||||
|
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
||||||
|
| hir::Node::ImplItem(hir::ImplItem {
|
||||||
|
kind: hir::ImplItemKind::Fn(_, body),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| hir::Node::TraitItem(hir::TraitItem {
|
||||||
|
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
||||||
|
..
|
||||||
|
})
|
||||||
|
| hir::Node::Expr(hir::Expr {
|
||||||
|
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
||||||
|
..
|
||||||
|
}),
|
||||||
|
) => {
|
||||||
|
for param in hir.body(*body).params {
|
||||||
|
param.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Some(hir::Node::Expr(hir::Expr {
|
||||||
|
kind:
|
||||||
|
hir::ExprKind::If(
|
||||||
|
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
||||||
|
then_block,
|
||||||
|
_,
|
||||||
|
),
|
||||||
|
..
|
||||||
|
})) if then_block.hir_id == *hir_id => {
|
||||||
|
let_.pat.walk(&mut find_compatible_candidates);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match &candidate_idents[..] {
|
||||||
|
[(ident, _ty)] => {
|
||||||
|
let sm = self.tcx.sess.source_map();
|
||||||
|
if let Some(stmt) = blk.stmts.last() {
|
||||||
|
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
||||||
|
let sugg = if sm.is_multiline(blk.span)
|
||||||
|
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
||||||
|
{
|
||||||
|
format!("\n{spacing}{ident}")
|
||||||
|
} else {
|
||||||
|
format!(" {ident}")
|
||||||
|
};
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
stmt_span.shrink_to_hi(),
|
||||||
|
format!("consider returning the local binding `{ident}`"),
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
let sugg = if sm.is_multiline(blk.span)
|
||||||
|
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
||||||
|
{
|
||||||
|
format!("\n{spacing} {ident}\n{spacing}")
|
||||||
|
} else {
|
||||||
|
format!(" {ident} ")
|
||||||
|
};
|
||||||
|
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
||||||
|
err.span_suggestion_verbose(
|
||||||
|
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
||||||
|
format!("consider returning the local binding `{ident}`"),
|
||||||
|
sugg,
|
||||||
|
Applicability::MaybeIncorrect,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
values if (1..3).contains(&values.len()) => {
|
||||||
|
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
||||||
|
err.span_note(spans, "consider returning one of these bindings");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -351,7 +351,7 @@ pub enum ObligationCauseCode<'tcx> {
|
|||||||
ConstPatternStructural,
|
ConstPatternStructural,
|
||||||
|
|
||||||
/// Computing common supertype in an if expression
|
/// Computing common supertype in an if expression
|
||||||
IfExpression(Box<IfExpressionCause>),
|
IfExpression(Box<IfExpressionCause<'tcx>>),
|
||||||
|
|
||||||
/// Computing common supertype of an if expression with no else counter-part
|
/// Computing common supertype of an if expression with no else counter-part
|
||||||
IfExpressionWithNoElse,
|
IfExpressionWithNoElse,
|
||||||
@ -498,12 +498,14 @@ pub struct MatchExpressionArmCause<'tcx> {
|
|||||||
pub opt_suggest_box_span: Option<Span>,
|
pub opt_suggest_box_span: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct IfExpressionCause {
|
#[derive(Lift, TypeFoldable, TypeVisitable)]
|
||||||
pub then: Span,
|
pub struct IfExpressionCause<'tcx> {
|
||||||
pub else_sp: Span,
|
pub then_id: hir::HirId,
|
||||||
pub outer: Option<Span>,
|
pub else_id: hir::HirId,
|
||||||
pub semicolon: Option<(Span, StatementAsExpression)>,
|
pub then_ty: Ty<'tcx>,
|
||||||
|
pub else_ty: Ty<'tcx>,
|
||||||
|
pub outer_span: Option<Span>,
|
||||||
pub opt_suggest_box_span: Option<Span>,
|
pub opt_suggest_box_span: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
|
|||||||
// Lift implementations
|
// Lift implementations
|
||||||
|
|
||||||
TrivialTypeTraversalAndLiftImpls! {
|
TrivialTypeTraversalAndLiftImpls! {
|
||||||
super::IfExpressionCause,
|
|
||||||
super::ImplSourceDiscriminantKindData,
|
super::ImplSourceDiscriminantKindData,
|
||||||
super::ImplSourcePointeeData,
|
super::ImplSourcePointeeData,
|
||||||
}
|
}
|
||||||
|
@ -216,13 +216,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
) -> (Span, Option<(Span, StatementAsExpression)>) {
|
) -> (Span, Option<(Span, StatementAsExpression)>) {
|
||||||
let arm = &arms[i];
|
let arm = &arms[i];
|
||||||
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
|
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
|
||||||
self.find_block_span(blk, prior_arm_ty)
|
(
|
||||||
|
self.find_block_span(blk),
|
||||||
|
prior_arm_ty
|
||||||
|
.and_then(|prior_arm_ty| self.could_remove_semicolon(blk, prior_arm_ty)),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
(arm.body.span, None)
|
(arm.body.span, None)
|
||||||
};
|
};
|
||||||
if semi_span.is_none() && i > 0 {
|
if semi_span.is_none() && i > 0 {
|
||||||
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
|
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
|
||||||
let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
|
let semi_span_prev = self.could_remove_semicolon(blk, arm_ty);
|
||||||
semi_span = semi_span_prev;
|
semi_span = semi_span_prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -313,7 +317,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
else_ty: Ty<'tcx>,
|
else_ty: Ty<'tcx>,
|
||||||
opt_suggest_box_span: Option<Span>,
|
opt_suggest_box_span: Option<Span>,
|
||||||
) -> ObligationCause<'tcx> {
|
) -> ObligationCause<'tcx> {
|
||||||
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
|
let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
|
||||||
// The `if`/`else` isn't in one line in the output, include some context to make it
|
// The `if`/`else` isn't in one line in the output, include some context to make it
|
||||||
// clear it is an if/else expression:
|
// clear it is an if/else expression:
|
||||||
// ```
|
// ```
|
||||||
@ -339,69 +343,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut remove_semicolon = None;
|
let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
|
||||||
let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
|
let block = block.peel_blocks();
|
||||||
let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty));
|
|
||||||
remove_semicolon = semi_sp;
|
// Avoid overlapping spans that aren't as readable:
|
||||||
if block.expr.is_none() && block.stmts.is_empty() {
|
// ```
|
||||||
// Avoid overlapping spans that aren't as readable:
|
// 2 | let x = if true {
|
||||||
// ```
|
// | _____________-
|
||||||
// 2 | let x = if true {
|
// 3 | | 3
|
||||||
// | _____________-
|
// | | - expected because of this
|
||||||
// 3 | | 3
|
// 4 | | } else {
|
||||||
// | | - expected because of this
|
// | |____________^
|
||||||
// 4 | | } else {
|
// 5 | ||
|
||||||
// | |____________^
|
// 6 | || };
|
||||||
// 5 | ||
|
// | || ^
|
||||||
// 6 | || };
|
// | ||_____|
|
||||||
// | || ^
|
// | |______if and else have incompatible types
|
||||||
// | ||_____|
|
// | expected integer, found `()`
|
||||||
// | |______if and else have incompatible types
|
// ```
|
||||||
// | expected integer, found `()`
|
// by not pointing at the entire expression:
|
||||||
// ```
|
// ```
|
||||||
// by not pointing at the entire expression:
|
// 2 | let x = if true {
|
||||||
// ```
|
// | ------- `if` and `else` have incompatible types
|
||||||
// 2 | let x = if true {
|
// 3 | 3
|
||||||
// | ------- `if` and `else` have incompatible types
|
// | - expected because of this
|
||||||
// 3 | 3
|
// 4 | } else {
|
||||||
// | - expected because of this
|
// | ____________^
|
||||||
// 4 | } else {
|
// 5 | |
|
||||||
// | ____________^
|
// 6 | | };
|
||||||
// 5 | |
|
// | |_____^ expected integer, found `()`
|
||||||
// 6 | | };
|
// ```
|
||||||
// | |_____^ expected integer, found `()`
|
if block.expr.is_none() && block.stmts.is_empty()
|
||||||
// ```
|
&& let Some(outer_span) = &mut outer_span
|
||||||
if outer_sp.is_some() {
|
{
|
||||||
outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
|
*outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
error_sp
|
|
||||||
|
(self.find_block_span(block), block.hir_id)
|
||||||
} else {
|
} else {
|
||||||
// shouldn't happen unless the parser has done something weird
|
(else_expr.span, else_expr.hir_id)
|
||||||
else_expr.span
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compute `Span` of `then` part of `if`-expression.
|
let then_id = if let ExprKind::Block(block, _) = &then_expr.kind {
|
||||||
let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
|
let block = block.peel_blocks();
|
||||||
let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty));
|
// Exclude overlapping spans
|
||||||
remove_semicolon = remove_semicolon.or(semi_sp);
|
|
||||||
if block.expr.is_none() && block.stmts.is_empty() {
|
if block.expr.is_none() && block.stmts.is_empty() {
|
||||||
outer_sp = None; // same as in `error_sp`; cleanup output
|
outer_span = None;
|
||||||
}
|
}
|
||||||
then_sp
|
block.hir_id
|
||||||
} else {
|
} else {
|
||||||
// shouldn't happen unless the parser has done something weird
|
then_expr.hir_id
|
||||||
then_expr.span
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Finally construct the cause:
|
// Finally construct the cause:
|
||||||
self.cause(
|
self.cause(
|
||||||
error_sp,
|
error_sp,
|
||||||
ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
|
ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
|
||||||
then: then_sp,
|
else_id,
|
||||||
else_sp: error_sp,
|
then_id,
|
||||||
outer: outer_sp,
|
then_ty,
|
||||||
semicolon: remove_semicolon,
|
else_ty,
|
||||||
|
outer_span,
|
||||||
opt_suggest_box_span,
|
opt_suggest_box_span,
|
||||||
})),
|
})),
|
||||||
)
|
)
|
||||||
@ -482,22 +484,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_block_span(
|
|
||||||
&self,
|
|
||||||
block: &'tcx hir::Block<'tcx>,
|
|
||||||
expected_ty: Option<Ty<'tcx>>,
|
|
||||||
) -> (Span, Option<(Span, StatementAsExpression)>) {
|
|
||||||
if let Some(expr) = &block.expr {
|
|
||||||
(expr.span, None)
|
|
||||||
} else if let Some(stmt) = block.stmts.last() {
|
|
||||||
// possibly incorrect trailing `;` in the else arm
|
|
||||||
(stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty)))
|
|
||||||
} else {
|
|
||||||
// empty block; point at its entirety
|
|
||||||
(block.span, None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
|
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
|
||||||
// we check if the different arms would work with boxed trait objects instead and
|
// we check if the different arms would work with boxed trait objects instead and
|
||||||
// provide a structured suggestion in that case.
|
// provide a structured suggestion in that case.
|
||||||
|
@ -30,17 +30,15 @@ use rustc_middle::ty::{
|
|||||||
};
|
};
|
||||||
use rustc_session::lint;
|
use rustc_session::lint;
|
||||||
use rustc_span::hygiene::DesugaringKind;
|
use rustc_span::hygiene::DesugaringKind;
|
||||||
use rustc_span::source_map::{original_sp, DUMMY_SP};
|
|
||||||
use rustc_span::symbol::{kw, sym, Ident};
|
use rustc_span::symbol::{kw, sym, Ident};
|
||||||
use rustc_span::{self, BytePos, Span};
|
use rustc_span::{Span, DUMMY_SP};
|
||||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
|
||||||
use rustc_trait_selection::traits::{
|
use rustc_trait_selection::traits::{
|
||||||
self, ObligationCause, ObligationCauseCode, StatementAsExpression, TraitEngine, TraitEngineExt,
|
self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
use std::iter;
|
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
@ -1059,84 +1057,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in super::super) fn could_remove_semicolon(
|
|
||||||
&self,
|
|
||||||
blk: &'tcx hir::Block<'tcx>,
|
|
||||||
expected_ty: Ty<'tcx>,
|
|
||||||
) -> Option<(Span, StatementAsExpression)> {
|
|
||||||
// Be helpful when the user wrote `{... expr;}` and
|
|
||||||
// taking the `;` off is enough to fix the error.
|
|
||||||
let last_stmt = blk.stmts.last()?;
|
|
||||||
let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else {
|
|
||||||
return None;
|
|
||||||
};
|
|
||||||
let last_expr_ty = self.node_ty(last_expr.hir_id);
|
|
||||||
let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) {
|
|
||||||
(ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _))
|
|
||||||
if last_def_id == exp_def_id =>
|
|
||||||
{
|
|
||||||
StatementAsExpression::CorrectType
|
|
||||||
}
|
|
||||||
(ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => {
|
|
||||||
debug!(
|
|
||||||
"both opaque, likely future {:?} {:?} {:?} {:?}",
|
|
||||||
last_def_id, last_bounds, exp_def_id, exp_bounds
|
|
||||||
);
|
|
||||||
|
|
||||||
let last_local_id = last_def_id.as_local()?;
|
|
||||||
let exp_local_id = exp_def_id.as_local()?;
|
|
||||||
|
|
||||||
match (
|
|
||||||
&self.tcx.hir().expect_item(last_local_id).kind,
|
|
||||||
&self.tcx.hir().expect_item(exp_local_id).kind,
|
|
||||||
) {
|
|
||||||
(
|
|
||||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }),
|
|
||||||
hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }),
|
|
||||||
) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| {
|
|
||||||
match (left, right) {
|
|
||||||
(
|
|
||||||
hir::GenericBound::Trait(tl, ml),
|
|
||||||
hir::GenericBound::Trait(tr, mr),
|
|
||||||
) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id()
|
|
||||||
&& ml == mr =>
|
|
||||||
{
|
|
||||||
true
|
|
||||||
}
|
|
||||||
(
|
|
||||||
hir::GenericBound::LangItemTrait(langl, _, _, argsl),
|
|
||||||
hir::GenericBound::LangItemTrait(langr, _, _, argsr),
|
|
||||||
) if langl == langr => {
|
|
||||||
// FIXME: consider the bounds!
|
|
||||||
debug!("{:?} {:?}", argsl, argsr);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}) =>
|
|
||||||
{
|
|
||||||
StatementAsExpression::NeedsBoxing
|
|
||||||
}
|
|
||||||
_ => StatementAsExpression::CorrectType,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => StatementAsExpression::CorrectType,
|
|
||||||
};
|
|
||||||
if (matches!(last_expr_ty.kind(), ty::Error(_))
|
|
||||||
|| self.can_sub(self.param_env, last_expr_ty, expected_ty).is_err())
|
|
||||||
&& matches!(needs_box, StatementAsExpression::CorrectType)
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let span = if last_stmt.span.from_expansion() {
|
|
||||||
let mac_call = original_sp(last_stmt.span, blk.span);
|
|
||||||
self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)?
|
|
||||||
} else {
|
|
||||||
last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1))
|
|
||||||
};
|
|
||||||
Some((span, needs_box))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Instantiates the given path, which must refer to an item with the given
|
// Instantiates the given path, which must refer to an item with the given
|
||||||
// number of type parameters and type.
|
// number of type parameters and type.
|
||||||
#[instrument(skip(self, span), level = "debug")]
|
#[instrument(skip(self, span), level = "debug")]
|
||||||
|
@ -3,7 +3,6 @@ use crate::astconv::AstConv;
|
|||||||
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
|
||||||
|
|
||||||
use rustc_ast::util::parser::ExprPrecedence;
|
use rustc_ast::util::parser::ExprPrecedence;
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
|
||||||
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind};
|
use rustc_hir::def::{CtorOf, DefKind};
|
||||||
@ -14,7 +13,7 @@ use rustc_hir::{
|
|||||||
use rustc_infer::infer::{self, TyCtxtInferExt};
|
use rustc_infer::infer::{self, TyCtxtInferExt};
|
||||||
use rustc_infer::traits::{self, StatementAsExpression};
|
use rustc_infer::traits::{self, StatementAsExpression};
|
||||||
use rustc_middle::lint::in_external_macro;
|
use rustc_middle::lint::in_external_macro;
|
||||||
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty, TypeVisitable};
|
use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty};
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
|
||||||
@ -904,117 +903,4 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn consider_returning_binding(
|
|
||||||
&self,
|
|
||||||
blk: &'tcx hir::Block<'tcx>,
|
|
||||||
expected_ty: Ty<'tcx>,
|
|
||||||
err: &mut Diagnostic,
|
|
||||||
) {
|
|
||||||
let mut shadowed = FxHashSet::default();
|
|
||||||
let mut candidate_idents = vec![];
|
|
||||||
let mut find_compatible_candidates = |pat: &hir::Pat<'_>| {
|
|
||||||
if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind
|
|
||||||
&& let Some(pat_ty) = self.typeck_results.borrow().node_type_opt(*hir_id)
|
|
||||||
{
|
|
||||||
let pat_ty = self.resolve_vars_if_possible(pat_ty);
|
|
||||||
if self.can_coerce(pat_ty, expected_ty)
|
|
||||||
&& !(pat_ty, expected_ty).references_error()
|
|
||||||
&& shadowed.insert(ident.name)
|
|
||||||
{
|
|
||||||
candidate_idents.push((*ident, pat_ty));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
};
|
|
||||||
|
|
||||||
let hir = self.tcx.hir();
|
|
||||||
for stmt in blk.stmts.iter().rev() {
|
|
||||||
let StmtKind::Local(local) = &stmt.kind else { continue; };
|
|
||||||
local.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
match hir.find(hir.get_parent_node(blk.hir_id)) {
|
|
||||||
Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => {
|
|
||||||
match hir.find(hir.get_parent_node(*hir_id)) {
|
|
||||||
Some(hir::Node::Arm(hir::Arm { pat, .. })) => {
|
|
||||||
pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
Some(
|
|
||||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. })
|
|
||||||
| hir::Node::ImplItem(hir::ImplItem {
|
|
||||||
kind: hir::ImplItemKind::Fn(_, body),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| hir::Node::TraitItem(hir::TraitItem {
|
|
||||||
kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)),
|
|
||||||
..
|
|
||||||
})
|
|
||||||
| hir::Node::Expr(hir::Expr {
|
|
||||||
kind: hir::ExprKind::Closure(hir::Closure { body, .. }),
|
|
||||||
..
|
|
||||||
}),
|
|
||||||
) => {
|
|
||||||
for param in hir.body(*body).params {
|
|
||||||
param.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(hir::Node::Expr(hir::Expr {
|
|
||||||
kind:
|
|
||||||
hir::ExprKind::If(
|
|
||||||
hir::Expr { kind: hir::ExprKind::Let(let_), .. },
|
|
||||||
then_block,
|
|
||||||
_,
|
|
||||||
),
|
|
||||||
..
|
|
||||||
})) if then_block.hir_id == *hir_id => {
|
|
||||||
let_.pat.walk(&mut find_compatible_candidates);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
match &candidate_idents[..] {
|
|
||||||
[(ident, _ty)] => {
|
|
||||||
let sm = self.tcx.sess.source_map();
|
|
||||||
if let Some(stmt) = blk.stmts.last() {
|
|
||||||
let stmt_span = sm.stmt_span(stmt.span, blk.span);
|
|
||||||
let sugg = if sm.is_multiline(blk.span)
|
|
||||||
&& let Some(spacing) = sm.indentation_before(stmt_span)
|
|
||||||
{
|
|
||||||
format!("\n{spacing}{ident}")
|
|
||||||
} else {
|
|
||||||
format!(" {ident}")
|
|
||||||
};
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
stmt_span.shrink_to_hi(),
|
|
||||||
format!("consider returning the local binding `{ident}`"),
|
|
||||||
sugg,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
let sugg = if sm.is_multiline(blk.span)
|
|
||||||
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
|
|
||||||
{
|
|
||||||
format!("\n{spacing} {ident}\n{spacing}")
|
|
||||||
} else {
|
|
||||||
format!(" {ident} ")
|
|
||||||
};
|
|
||||||
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi();
|
|
||||||
err.span_suggestion_verbose(
|
|
||||||
sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
|
|
||||||
format!("consider returning the local binding `{ident}`"),
|
|
||||||
sugg,
|
|
||||||
Applicability::MachineApplicable,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
values if (1..3).contains(&values.len()) => {
|
|
||||||
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
|
|
||||||
err.span_note(spans, "consider returning one of these bindings");
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,6 @@ LL | | break 0u8;
|
|||||||
LL | | };
|
LL | | };
|
||||||
| |_________- enclosing `async` block
|
| |_________- enclosing `async` block
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
|
|
||||||
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
|
|
||||||
|
|
|
||||||
LL | let _: &dyn Future<Output = ()> = █
|
|
||||||
| ^^^^^^ expected `()`, found `u8`
|
|
||||||
|
|
|
||||||
= note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/async-block-control-flow-static-semantics.rs:21:58
|
--> $DIR/async-block-control-flow-static-semantics.rs:21:58
|
||||||
|
|
|
|
||||||
@ -40,7 +32,7 @@ LL | | }
|
|||||||
| |_^ expected `u8`, found `()`
|
| |_^ expected `u8`, found `()`
|
||||||
|
|
||||||
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
|
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
|
||||||
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
|
--> $DIR/async-block-control-flow-static-semantics.rs:26:39
|
||||||
|
|
|
|
||||||
LL | let _: &dyn Future<Output = ()> = █
|
LL | let _: &dyn Future<Output = ()> = █
|
||||||
| ^^^^^^ expected `()`, found `u8`
|
| ^^^^^^ expected `()`, found `u8`
|
||||||
@ -55,6 +47,14 @@ LL | fn return_targets_async_block_not_fn() -> u8 {
|
|||||||
| |
|
| |
|
||||||
| implicitly returns `()` as its body has no tail or `return` expression
|
| implicitly returns `()` as its body has no tail or `return` expression
|
||||||
|
|
||||||
|
error[E0271]: type mismatch resolving `<impl Future<Output = u8> as Future>::Output == ()`
|
||||||
|
--> $DIR/async-block-control-flow-static-semantics.rs:17:39
|
||||||
|
|
|
||||||
|
LL | let _: &dyn Future<Output = ()> = █
|
||||||
|
| ^^^^^^ expected `()`, found `u8`
|
||||||
|
|
|
||||||
|
= note: required for the cast from `impl Future<Output = u8>` to the object type `dyn Future<Output = ()>`
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/async-block-control-flow-static-semantics.rs:47:44
|
--> $DIR/async-block-control-flow-static-semantics.rs:47:44
|
||||||
|
|
|
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
// run-rustfix
|
|
||||||
|
|
||||||
#![allow(unused)]
|
|
||||||
|
|
||||||
fn a(i: i32) -> i32 { i }
|
|
||||||
//~^ ERROR mismatched types
|
|
||||||
|
|
||||||
fn b(opt_str: Option<String>) {
|
|
||||||
let s: String = if let Some(s) = opt_str {
|
|
||||||
s
|
|
||||||
//~^ ERROR mismatched types
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn c() -> Option<i32> {
|
|
||||||
//~^ ERROR mismatched types
|
|
||||||
let x = Some(1);
|
|
||||||
x
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {}
|
|
@ -1,5 +1,3 @@
|
|||||||
// run-rustfix
|
|
||||||
|
|
||||||
#![allow(unused)]
|
#![allow(unused)]
|
||||||
|
|
||||||
fn a(i: i32) -> i32 {}
|
fn a(i: i32) -> i32 {}
|
||||||
@ -18,4 +16,12 @@ fn c() -> Option<i32> {
|
|||||||
let x = Some(1);
|
let x = Some(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn d(opt_str: Option<String>) {
|
||||||
|
let s: String = if let Some(s) = opt_str {
|
||||||
|
//~^ ERROR mismatched types
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/return-bindings.rs:5:17
|
--> $DIR/return-bindings.rs:3:17
|
||||||
|
|
|
|
||||||
LL | fn a(i: i32) -> i32 {}
|
LL | fn a(i: i32) -> i32 {}
|
||||||
| - ^^^ expected `i32`, found `()`
|
| - ^^^ expected `i32`, found `()`
|
||||||
@ -12,7 +12,7 @@ LL | fn a(i: i32) -> i32 { i }
|
|||||||
| +
|
| +
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/return-bindings.rs:9:46
|
--> $DIR/return-bindings.rs:7:46
|
||||||
|
|
|
|
||||||
LL | let s: String = if let Some(s) = opt_str {
|
LL | let s: String = if let Some(s) = opt_str {
|
||||||
| ______________________________________________^
|
| ______________________________________________^
|
||||||
@ -28,7 +28,7 @@ LL ~
|
|||||||
|
|
|
|
||||||
|
|
||||||
error[E0308]: mismatched types
|
error[E0308]: mismatched types
|
||||||
--> $DIR/return-bindings.rs:16:11
|
--> $DIR/return-bindings.rs:14:11
|
||||||
|
|
|
|
||||||
LL | fn c() -> Option<i32> {
|
LL | fn c() -> Option<i32> {
|
||||||
| - ^^^^^^^^^^^ expected enum `Option`, found `()`
|
| - ^^^^^^^^^^^ expected enum `Option`, found `()`
|
||||||
@ -43,6 +43,22 @@ LL ~ let x = Some(1);
|
|||||||
LL + x
|
LL + x
|
||||||
|
|
|
|
||||||
|
|
||||||
error: aborting due to 3 previous errors
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/return-bindings.rs:20:46
|
||||||
|
|
|
||||||
|
LL | let s: String = if let Some(s) = opt_str {
|
||||||
|
| ______________________________________________^
|
||||||
|
LL | |
|
||||||
|
LL | | } else {
|
||||||
|
| |_____^ expected struct `String`, found `()`
|
||||||
|
|
|
||||||
|
help: consider returning the local binding `s`
|
||||||
|
|
|
||||||
|
LL ~ let s: String = if let Some(s) = opt_str {
|
||||||
|
LL + s
|
||||||
|
LL ~
|
||||||
|
|
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user