Skip to content

Commit ca7df9a

Browse files
committed
migrate: for_loops_over_fallibles.rs
1 parent 3c1a1f3 commit ca7df9a

File tree

3 files changed

+85
-46
lines changed

3 files changed

+85
-46
lines changed

compiler/rustc_error_messages/locales/en-US/lint.ftl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ lint_enum_intrinsics_mem_variant =
1616
lint_expectation = this lint expectation is unfulfilled
1717
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
1818
19+
lint_for_loops_over_fallibles =
20+
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
21+
.suggestion = consider using `if let` to clear intent
22+
.remove_next = to iterate over `{$recv_snip}` remove the call to `next`
23+
.use_while_let = to check pattern in a loop use `while let`
24+
.use_question_mark = consider unwrapping the `Result` with `?` to iterate over its contents
25+
1926
lint_non_binding_let_on_sync_lock =
2027
non-binding let on a synchronization lock
2128

compiler/rustc_lint/src/for_loops_over_fallibles.rs

Lines changed: 29 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
use crate::{LateContext, LateLintPass, LintContext};
1+
#![deny(rustc::untranslatable_diagnostic)]
2+
#![deny(rustc::diagnostic_outside_of_impl)]
3+
use crate::{
4+
lints::{
5+
ForLoopsOverFalliblesDiag, ForLoopsOverFalliblesLoopSub, ForLoopsOverFalliblesQuestionMark,
6+
ForLoopsOverFalliblesSuggestion,
7+
},
8+
LateContext, LateLintPass, LintContext,
9+
};
210

311
use hir::{Expr, Pat};
4-
use rustc_errors::{Applicability, DelayDm};
512
use rustc_hir as hir;
613
use rustc_infer::{infer::TyCtxtInferExt, traits::ObligationCause};
714
use rustc_middle::ty::{self, List};
@@ -53,53 +60,29 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
5360
_ => return,
5461
};
5562

56-
let msg = DelayDm(|| {
57-
format!(
58-
"for loop over {article} `{ty}`. This is more readably written as an `if let` statement",
59-
)
60-
});
61-
62-
cx.struct_span_lint(FOR_LOOPS_OVER_FALLIBLES, arg.span, msg, |lint| {
63-
if let Some(recv) = extract_iterator_next_call(cx, arg)
63+
let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
6464
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
6565
{
66-
lint.span_suggestion(
67-
recv.span.between(arg.span.shrink_to_hi()),
68-
format!("to iterate over `{recv_snip}` remove the call to `next`"),
69-
".by_ref()",
70-
Applicability::MaybeIncorrect
71-
);
66+
ForLoopsOverFalliblesLoopSub::RemoveNext { suggestion: recv.span.between(arg.span.shrink_to_hi()), recv_snip }
7267
} else {
73-
lint.multipart_suggestion_verbose(
74-
"to check pattern in a loop use `while let`",
75-
vec![
76-
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
77-
(expr.span.with_hi(pat.span.lo()), format!("while let {var}(")),
78-
(pat.span.between(arg.span), ") = ".to_string()),
79-
],
80-
Applicability::MaybeIncorrect
81-
);
82-
}
83-
84-
if suggest_question_mark(cx, adt, substs, expr.span) {
85-
lint.span_suggestion(
86-
arg.span.shrink_to_hi(),
87-
"consider unwrapping the `Result` with `?` to iterate over its contents",
88-
"?",
89-
Applicability::MaybeIncorrect,
90-
);
91-
}
92-
93-
lint.multipart_suggestion_verbose(
94-
"consider using `if let` to clear intent",
95-
vec![
96-
// NB can't use `until` here because `expr.span` and `pat.span` have different syntax contexts
97-
(expr.span.with_hi(pat.span.lo()), format!("if let {var}(")),
98-
(pat.span.between(arg.span), ") = ".to_string()),
99-
],
100-
Applicability::MaybeIncorrect,
101-
)
102-
})
68+
ForLoopsOverFalliblesLoopSub::UseWhileLet { start_span: expr.span.with_hi(pat.span.lo()), end_span: pat.span.between(arg.span), var }
69+
} ;
70+
let question_mark = if suggest_question_mark(cx, adt, substs, expr.span) {
71+
Some(ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() })
72+
} else {
73+
None
74+
};
75+
let suggestion = ForLoopsOverFalliblesSuggestion {
76+
var,
77+
start_span: expr.span.with_hi(pat.span.lo()),
78+
end_span: pat.span.between(arg.span),
79+
};
80+
81+
cx.emit_spanned_lint(
82+
FOR_LOOPS_OVER_FALLIBLES,
83+
arg.span,
84+
ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
85+
);
10386
}
10487
}
10588

compiler/rustc_lint/src/lints.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,55 @@ impl<'a> DecorateLint<'a, ()> for Expectation<'_> {
333333
}
334334
}
335335

336+
// for_loops_over_fallibles.rs
337+
#[derive(LintDiagnostic)]
338+
#[diag(lint_for_loops_over_fallibles)]
339+
pub struct ForLoopsOverFalliblesDiag<'a> {
340+
pub article: &'static str,
341+
pub ty: &'static str,
342+
#[subdiagnostic]
343+
pub sub: ForLoopsOverFalliblesLoopSub<'a>,
344+
#[subdiagnostic]
345+
pub question_mark: Option<ForLoopsOverFalliblesQuestionMark>,
346+
#[subdiagnostic]
347+
pub suggestion: ForLoopsOverFalliblesSuggestion<'a>,
348+
}
349+
350+
#[derive(Subdiagnostic)]
351+
pub enum ForLoopsOverFalliblesLoopSub<'a> {
352+
#[suggestion(remove_next, code = ".by_ref()", applicability = "maybe-incorrect")]
353+
RemoveNext {
354+
#[primary_span]
355+
suggestion: Span,
356+
recv_snip: String,
357+
},
358+
#[multipart_suggestion(use_while_let, applicability = "maybe-incorrect")]
359+
UseWhileLet {
360+
#[suggestion_part(code = "while let {var}(")]
361+
start_span: Span,
362+
#[suggestion_part(code = ") = ")]
363+
end_span: Span,
364+
var: &'a str,
365+
},
366+
}
367+
368+
#[derive(Subdiagnostic)]
369+
#[suggestion(use_question_mark, code = "?", applicability = "maybe-incorrect")]
370+
pub struct ForLoopsOverFalliblesQuestionMark {
371+
#[primary_span]
372+
pub suggestion: Span,
373+
}
374+
375+
#[derive(Subdiagnostic)]
376+
#[multipart_suggestion(suggestion, applicability = "maybe-incorrect")]
377+
pub struct ForLoopsOverFalliblesSuggestion<'a> {
378+
pub var: &'a str,
379+
#[suggestion_part(code = "if let {var}(")]
380+
pub start_span: Span,
381+
#[suggestion_part(code = ") = ")]
382+
pub end_span: Span,
383+
}
384+
336385
// internal.rs
337386
#[derive(LintDiagnostic)]
338387
#[diag(lint_default_hash_types)]

0 commit comments

Comments
 (0)