migrate: for_loops_over_fallibles.rs

This commit is contained in:
Rejyr 2022-11-09 19:34:49 -05:00
parent 3c1a1f3643
commit ca7df9a2a9
3 changed files with 84 additions and 45 deletions

View File

@ -16,6 +16,13 @@ lint_enum_intrinsics_mem_variant =
lint_expectation = this lint expectation is unfulfilled
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
lint_for_loops_over_fallibles =
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
.suggestion = consider using `if let` to clear intent
.remove_next = to iterate over `{$recv_snip}` remove the call to `next`
.use_while_let = to check pattern in a loop use `while let`
.use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
lint_non_binding_let_on_sync_lock =
non-binding let on a synchronization lock

View File

@ -1,7 +1,14 @@
use crate::{LateContext, LateLintPass, LintContext};
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
use crate::{
lints::{
ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
ForLoopsOverFalliblesSuggestion,
},
LateContext, LateLintPass, LintContext,
};
use hir::{Expr, Pat};
use rustc_errors::{Applicability, DelayDm};
use rustc_hir as hir;
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
use rustc_middle::ty::{self, List};
@ -53,53 +60,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
_ => return,
};
let msg = DelayDm(|| {
format!(
"for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
)
});
cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
if let Some(recv) = extract_iterator_next_call(cx, arg)
let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
lint.span_suggestion(
recv.span.between(arg.span.shrink_to_hi()),
format!("to iterate over `{recv_snip}` remove the call to `next`"),
".by_ref()",
Applicability::MaybeIncorrect
);
ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
} else {
lint.multipart_suggestion_verbose(
"to check pattern in a loop use `while let`",
vec![
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
(expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
(pat.span.between(arg.span), ") = ".to_string()),
],
Applicability::MaybeIncorrect
);
}
ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
} ;
let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
} else {
None
};
let suggestion = ForLoopsOverFalliblesSuggestion {
var,
start_span: expr.span.with_hi(pat.span.lo()),
end_span: pat.span.between(arg.span),
};
if suggest_question_mark(cx, adt, substs, expr.span) {
lint.span_suggestion(
arg.span.shrink_to_hi(),
"consider unwrapping the `Result` with `?` to iterate over its contents",
"?",
Applicability::MaybeIncorrect,
);
}
lint.multipart_suggestion_verbose(
"consider using `if let` to clear intent",
vec![
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
(expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
(pat.span.between(arg.span), ") = ".to_string()),
],
Applicability::MaybeIncorrect,
)
})
cx.emit_spanned_lint(
FOR_LOOPS_OVER_FALLIBLES,
arg.span,
ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
);
}
}

View File

@ -333,6 +333,55 @@ impl<'a> DecorateLint<'a, ()> for Expectation<'_> {
}
}
// for_loops_over_fallibles.rs
#[derive(LintDiagnostic)]
#[diag(lint_for_loops_over_fallibles)]
pub struct ForLoopsOverFalliblesDiag<'a> {
pub article: &'static str,
pub ty: &'static str,
#[subdiagnostic]
pub sub: ForLoopsOverFalliblesLoopSub<'a>,
#[subdiagnostic]
pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>,
#[subdiagnostic]
pub suggestion: ForLoopsOverFalliblesSuggestion<'a>,
}
#[derive(Subdiagnostic)]
pub enum ForLoopsOverFalliblesLoopSub<'a> {
#[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
RemoveNext {
#[primary_span]
suggestion: Span,
recv_snip: String,
},
#[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
UseWhileLet {
#[suggestion_part(code = "while let {var}(")]
start_span: Span,
#[suggestion_part(code = ") = ")]
end_span: Span,
var: &'a str,
},
}
#[derive(Subdiagnostic)]
#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
pub struct ForLoopsOverFalliblesQuestionMark {
#[primary_span]
pub suggestion: Span,
}
#[derive(Subdiagnostic)]
#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
pub struct ForLoopsOverFalliblesSuggestion<'a> {
pub var: &'a str,
#[suggestion_part(code = "if let {var}(")]
pub start_span: Span,
#[suggestion_part(code = ") = ")]
pub end_span: Span,
}
// internal.rs
#[derive(LintDiagnostic)]
#[diag(lint_default_hash_types)]