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
commit b165a4c280
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 41 additions and 5 deletions

View file

@ -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 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
&& 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 },
);
}

View file

@ -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]
}

View file

@ -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