Rollup merge of #140801 - xizheyin:issue-140747, r=SparrowLii

Use span before macro expansion in lint for-loops-over-falibles

Fixes #140747

I think there are going to be a lot of cases where macros are expanded in the compiler resulting in span offsets, and I'd like to know how that's typically handled. Does it have to be handled specially every time?
This commit is contained in:
Stuart Cook
2025-05-09 16:25:03 +10:00
committed by GitHub
3 changed files with 41 additions and 5 deletions
@@ -49,6 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let Some((pat, arg)) = extract_for_loop(expr) else { return };
let arg_span = arg.span.source_callsite();
let ty = cx.typeck_results().expr_ty(arg);
let (adt, args, ref_mutability) = match ty.kind() {
@@ -78,27 +80,27 @@ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
{
ForLoopsOverFalliblesLoopSub::RemoveNext {
suggestion: recv.span.between(arg.span.shrink_to_hi()),
suggestion: recv.span.between(arg_span.shrink_to_hi()),
recv_snip,
}
} else {
ForLoopsOverFalliblesLoopSub::UseWhileLet {
start_span: expr.span.with_hi(pat.span.lo()),
end_span: pat.span.between(arg.span),
end_span: pat.span.between(arg_span),
var,
}
};
let question_mark = suggest_question_mark(cx, adt, args, expr.span)
.then(|| ForLoopsOverFalliblesQuestionMark { suggestion: arg.span.shrink_to_hi() });
.then(|| ForLoopsOverFalliblesQuestionMark { suggestion: arg_span.shrink_to_hi() });
let suggestion = ForLoopsOverFalliblesSuggestion {
var,
start_span: expr.span.with_hi(pat.span.lo()),
end_span: pat.span.between(arg.span),
end_span: pat.span.between(arg_span),
};
cx.emit_span_lint(
FOR_LOOPS_OVER_FALLIBLES,
arg.span,
arg_span,
ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion },
);
}
@@ -0,0 +1,10 @@
#![forbid(for_loops_over_fallibles)]
fn main() {
macro_rules! x {
() => {
None::<i32>
};
}
for _ in x! {} {} //~ ERROR for loop over an `Option`. This is more readably written as an `if let` statement [for_loops_over_fallibles]
}
@@ -0,0 +1,24 @@
error: for loop over an `Option`. This is more readably written as an `if let` statement
--> $DIR/macro-issue-140747.rs:9:14
|
LL | for _ in x! {} {}
| ^^^^^
|
note: the lint level is defined here
--> $DIR/macro-issue-140747.rs:1:11
|
LL | #![forbid(for_loops_over_fallibles)]
| ^^^^^^^^^^^^^^^^^^^^^^^^
help: to check pattern in a loop use `while let`
|
LL - for _ in x! {} {}
LL + while let Some(_) = x! {} {}
|
help: consider using `if let` to clear intent
|
LL - for _ in x! {} {}
LL + if let Some(_) = x! {} {}
|
error: aborting due to 1 previous error