Port SuggestRemoveSemiOrReturnBinding

This commit is contained in:
IQuant 2023-01-28 19:41:14 +03:00
parent 6fa4c7d89c
commit 9f06c3d87f
4 changed files with 97 additions and 46 deletions

View File

@ -330,3 +330,8 @@ infer_ril_static_introduced_by = "`'static` lifetime requirement introduced by t
infer_where_remove = remove the `where` clause infer_where_remove = remove the `where` clause
infer_where_copy_predicates = copy the `where` clause predicates from the trait infer_where_copy_predicates = copy the `where` clause predicates from the trait
infer_srs_remove_and_box = consider removing this semicolon and boxing the expressions
infer_srs_remove = consider removing this semicolon
infer_srs_add = consider returning the local binding `{$ident}`
infer_srs_add_one = consider returning one of these bindings

View File

@ -1006,3 +1006,47 @@ pub enum WhereClauseSuggestions {
trait_predicates: String, trait_predicates: String,
}, },
} }
#[derive(Subdiagnostic)]
pub enum SuggestRemoveSemiOrReturnBinding {
#[multipart_suggestion(infer_srs_remove_and_box, applicability = "machine-applicable")]
RemoveAndBox {
#[suggestion_part(code = "Box::new(")]
first_lo: Span,
#[suggestion_part(code = ")")]
first_hi: Span,
#[suggestion_part(code = "Box::new(")]
second_lo: Span,
#[suggestion_part(code = ")")]
second_hi: Span,
#[suggestion_part(code = "")]
sp: Span,
},
#[suggestion(
infer_srs_remove,
style = "short",
code = "",
applicability = "machine-applicable"
)]
Remove {
#[primary_span]
sp: Span,
},
#[suggestion(
infer_srs_add,
style = "verbose",
code = "{code}",
applicability = "maybe-incorrect"
)]
Add {
#[primary_span]
sp: Span,
code: String,
ident: Ident,
},
#[note(infer_srs_add_one)]
AddOne {
#[primary_span]
spans: MultiSpan,
},
}

View File

@ -750,15 +750,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}; };
let msg = "`match` arms have incompatible types"; let msg = "`match` arms have incompatible types";
err.span_label(outer, msg); err.span_label(outer, msg);
self.suggest_remove_semi_or_return_binding( if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
err,
prior_arm_block_id, prior_arm_block_id,
prior_arm_ty, prior_arm_ty,
prior_arm_span, prior_arm_span,
arm_block_id, arm_block_id,
arm_ty, arm_ty,
arm_span, arm_span,
); ) {
err.subdiagnostic(subdiag);
}
if let Some(ret_sp) = opt_suggest_box_span { if let Some(ret_sp) = opt_suggest_box_span {
// Get return type span and point to it. // Get return type span and point to it.
self.suggest_boxing_for_return_impl_trait( self.suggest_boxing_for_return_impl_trait(
@ -783,15 +784,16 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
if let Some(sp) = outer_span { 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");
} }
self.suggest_remove_semi_or_return_binding( if let Some(subdiag) = self.suggest_remove_semi_or_return_binding(
err,
Some(then_id), Some(then_id),
then_ty, then_ty,
then_span, then_span,
Some(else_id), Some(else_id),
else_ty, else_ty,
else_span, else_span,
); ) {
err.subdiagnostic(subdiag);
}
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,

View File

@ -11,21 +11,20 @@ use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable}; use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
use rustc_span::{sym, BytePos, Span}; use rustc_span::{sym, BytePos, Span};
use crate::errors::SuggAddLetForLetChains; use crate::errors::{SuggAddLetForLetChains, SuggestRemoveSemiOrReturnBinding};
use super::TypeErrCtxt; use super::TypeErrCtxt;
impl<'tcx> TypeErrCtxt<'_, 'tcx> { impl<'tcx> TypeErrCtxt<'_, 'tcx> {
pub(super) fn suggest_remove_semi_or_return_binding( pub(super) fn suggest_remove_semi_or_return_binding(
&self, &self,
err: &mut Diagnostic,
first_id: Option<hir::HirId>, first_id: Option<hir::HirId>,
first_ty: Ty<'tcx>, first_ty: Ty<'tcx>,
first_span: Span, first_span: Span,
second_id: Option<hir::HirId>, second_id: Option<hir::HirId>,
second_ty: Ty<'tcx>, second_ty: Ty<'tcx>,
second_span: Span, second_span: Span,
) { ) -> Option<SuggestRemoveSemiOrReturnBinding> {
let remove_semicolon = [ let remove_semicolon = [
(first_id, self.resolve_vars_if_possible(second_ty)), (first_id, self.resolve_vars_if_possible(second_ty)),
(second_id, self.resolve_vars_if_possible(first_ty)), (second_id, self.resolve_vars_if_possible(first_ty)),
@ -37,35 +36,29 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}); });
match remove_semicolon { match remove_semicolon {
Some((sp, StatementAsExpression::NeedsBoxing)) => { Some((sp, StatementAsExpression::NeedsBoxing)) => {
err.multipart_suggestion( Some(SuggestRemoveSemiOrReturnBinding::RemoveAndBox {
"consider removing this semicolon and boxing the expressions", first_lo: first_span.shrink_to_lo(),
vec![ first_hi: first_span.shrink_to_hi(),
(first_span.shrink_to_lo(), "Box::new(".to_string()), second_lo: second_span.shrink_to_lo(),
(first_span.shrink_to_hi(), ")".to_string()), second_hi: second_span.shrink_to_hi(),
(second_span.shrink_to_lo(), "Box::new(".to_string()), sp,
(second_span.shrink_to_hi(), ")".to_string()), })
(sp, String::new()),
],
Applicability::MachineApplicable,
);
} }
Some((sp, StatementAsExpression::CorrectType)) => { Some((sp, StatementAsExpression::CorrectType)) => {
err.span_suggestion_short( Some(SuggestRemoveSemiOrReturnBinding::Remove { sp })
sp,
"consider removing this semicolon",
"",
Applicability::MachineApplicable,
);
} }
None => { None => {
let mut ret = None;
for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] {
if let Some(id) = id if let Some(id) = id
&& let hir::Node::Block(blk) = self.tcx.hir().get(id) && let hir::Node::Block(blk) = self.tcx.hir().get(id)
&& self.consider_returning_binding(blk, ty, err) && let Some(diag) = self.consider_returning_binding_diag(blk, ty)
{ {
ret = Some(diag);
break; break;
} }
} }
ret
} }
} }
} }
@ -655,16 +648,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
/// Suggest returning a local binding with a compatible type if the block /// Suggest returning a local binding with a compatible type if the block
/// has no return expression. /// has no return expression.
pub fn consider_returning_binding( pub fn consider_returning_binding_diag(
&self, &self,
blk: &'tcx hir::Block<'tcx>, blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>, expected_ty: Ty<'tcx>,
err: &mut Diagnostic, ) -> Option<SuggestRemoveSemiOrReturnBinding> {
) -> bool {
let blk = blk.innermost_block(); let blk = blk.innermost_block();
// Do not suggest if we have a tail expr. // Do not suggest if we have a tail expr.
if blk.expr.is_some() { if blk.expr.is_some() {
return false; return None;
} }
let mut shadowed = FxIndexSet::default(); let mut shadowed = FxIndexSet::default();
let mut candidate_idents = vec![]; let mut candidate_idents = vec![];
@ -733,7 +725,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
match &candidate_idents[..] { match &candidate_idents[..] {
[(ident, _ty)] => { [(ident, _ty)] => {
let sm = self.tcx.sess.source_map(); let sm = self.tcx.sess.source_map();
if let Some(stmt) = blk.stmts.last() { let (span, sugg) = if let Some(stmt) = blk.stmts.last() {
let stmt_span = sm.stmt_span(stmt.span, blk.span); let stmt_span = sm.stmt_span(stmt.span, blk.span);
let sugg = if sm.is_multiline(blk.span) let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(stmt_span) && let Some(spacing) = sm.indentation_before(stmt_span)
@ -742,12 +734,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
} else { } else {
format!(" {ident}") format!(" {ident}")
}; };
err.span_suggestion_verbose( (stmt_span.shrink_to_hi(), sugg)
stmt_span.shrink_to_hi(),
format!("consider returning the local binding `{ident}`"),
sugg,
Applicability::MaybeIncorrect,
);
} else { } else {
let sugg = if sm.is_multiline(blk.span) let sugg = if sm.is_multiline(blk.span)
&& let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo())
@ -757,21 +744,34 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
format!(" {ident} ") format!(" {ident} ")
}; };
let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); 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), sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span),
format!("consider returning the local binding `{ident}`"),
sugg, sugg,
Applicability::MaybeIncorrect, )
); };
} Some(SuggestRemoveSemiOrReturnBinding::Add { sp: span, code: sugg, ident: *ident })
true
} }
values if (1..3).contains(&values.len()) => { values if (1..3).contains(&values.len()) => {
let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>();
err.span_note(spans, "consider returning one of these bindings"); Some(SuggestRemoveSemiOrReturnBinding::AddOne { spans: spans.into() })
}
_ => None,
}
}
pub fn consider_returning_binding(
&self,
blk: &'tcx hir::Block<'tcx>,
expected_ty: Ty<'tcx>,
err: &mut Diagnostic,
) -> bool {
let diag = self.consider_returning_binding_diag(blk, expected_ty);
match diag {
Some(diag) => {
err.subdiagnostic(diag);
true true
} }
_ => false, None => false,
} }
} }
} }