1
Fork 0

Detect bindings assigned blocks without tail expressions in trait errors

Address  #44173 for trait errors.
This commit is contained in:
Esteban Küber 2023-01-06 02:43:16 +00:00
parent 1983a627b3
commit 1fa6ada9dd
4 changed files with 172 additions and 28 deletions

View file

@ -771,7 +771,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
),
}
};
self.check_for_binding_assigned_block_without_tail_expression(
&obligation,
&mut err,
trait_predicate,
);
if self.suggest_add_reference_to_arg(
&obligation,
&mut err,
@ -2267,23 +2271,7 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
if let (Some(body_id), Some(ty::subst::GenericArgKind::Type(_))) =
(body_id, subst.map(|subst| subst.unpack()))
{
struct FindExprBySpan<'hir> {
span: Span,
result: Option<&'hir hir::Expr<'hir>>,
}
impl<'v> hir::intravisit::Visitor<'v> for FindExprBySpan<'v> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
if self.span == ex.span {
self.result = Some(ex);
} else {
hir::intravisit::walk_expr(self, ex);
}
}
}
let mut expr_finder = FindExprBySpan { span, result: None };
let mut expr_finder = FindExprBySpan::new(span);
expr_finder.visit_expr(&self.tcx.hir().body(body_id).value);
if let Some(hir::Expr {
@ -2770,6 +2758,36 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
}
}
/// Crude way of getting back an `Expr` from a `Span`.
pub struct FindExprBySpan<'hir> {
pub span: Span,
pub result: Option<&'hir hir::Expr<'hir>>,
pub ty_result: Option<&'hir hir::Ty<'hir>>,
}
impl<'hir> FindExprBySpan<'hir> {
fn new(span: Span) -> Self {
Self { span, result: None, ty_result: None }
}
}
impl<'v> Visitor<'v> for FindExprBySpan<'v> {
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
if self.span == ex.span {
self.result = Some(ex);
} else {
hir::intravisit::walk_expr(self, ex);
}
}
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
if self.span == ty.span {
self.ty_result = Some(ty);
} else {
hir::intravisit::walk_ty(self, ty);
}
}
}
/// Look for type `param` in an ADT being used only through a reference to confirm that suggesting
/// `param: ?Sized` would be a valid constraint.
struct FindTypeParam {

View file

@ -1,6 +1,9 @@
// ignore-tidy-filelength
use super::{DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation};
use super::{
DefIdOrName, FindExprBySpan, Obligation, ObligationCause, ObligationCauseCode,
PredicateObligation,
};
use crate::autoderef::Autoderef;
use crate::infer::InferCtxt;
@ -196,6 +199,13 @@ pub trait TypeErrCtxtExt<'tcx> {
trait_pred: ty::PolyTraitPredicate<'tcx>,
) -> bool;
fn check_for_binding_assigned_block_without_tail_expression(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
);
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,
@ -1032,6 +1042,66 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
true
}
fn check_for_binding_assigned_block_without_tail_expression(
&self,
obligation: &PredicateObligation<'tcx>,
err: &mut Diagnostic,
trait_pred: ty::PolyTraitPredicate<'tcx>,
) {
let mut span = obligation.cause.span;
while span.from_expansion() {
// Remove all the desugaring and macro contexts.
span.remove_mark();
}
let mut expr_finder = FindExprBySpan::new(span);
let Some(hir::Node::Expr(body)) = self.tcx.hir().find(obligation.cause.body_id) else { return; };
expr_finder.visit_expr(&body);
let Some(expr) = expr_finder.result else { return; };
let Some(typeck) = &self.typeck_results else { return; };
let Some(ty) = typeck.expr_ty_adjusted_opt(expr) else { return; };
if !ty.is_unit() {
return;
};
let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = expr.kind else { return; };
let hir::def::Res::Local(hir_id) = path.res else { return; };
let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(hir_id) else {
return;
};
let Some(hir::Node::Local(hir::Local {
ty: None,
init: Some(init),
..
})) = self.tcx.hir().find_parent(pat.hir_id) else { return; };
let hir::ExprKind::Block(block, None) = init.kind else { return; };
if block.expr.is_some() {
return;
}
let [.., stmt] = block.stmts else {
err.span_help(block.span, "this empty block is missing a tail expression");
return;
};
let hir::StmtKind::Semi(tail_expr) = stmt.kind else { return; };
let Some(ty) = typeck.expr_ty_opt(tail_expr) else {
err.span_help(block.span, "this block is missing a tail expression");
return;
};
let ty = self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(ty));
let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, ty));
let new_obligation =
self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self);
if self.predicate_must_hold_modulo_regions(&new_obligation) {
err.span_suggestion_verbose(
stmt.span.with_lo(tail_expr.span.hi()),
"remove this semicolon",
"",
Applicability::MachineApplicable,
);
} else {
err.span_help(block.span, "this block is missing a tail expression");
}
}
fn suggest_add_reference_to_arg(
&self,
obligation: &PredicateObligation<'tcx>,