Skip to content

Commit 4af1c31

Browse files
authored
Rollup merge of #125156 - zachs18:for_loops_over_fallibles_behind_refs, r=Nilstrieb
Expand `for_loops_over_fallibles` lint to lint on fallibles behind references. Extends the scope of the (warn-by-default) lint `for_loops_over_fallibles` from just `for _ in x` where `x: Option<_>/Result<_, _>` to also cover `x: &(mut) Option<_>/Result<_>` ```rs fn main() { // Current lints for _ in Some(42) {} for _ in Ok::<_, i32>(42) {} // New lints for _ in &Some(42) {} for _ in &mut Some(42) {} for _ in &Ok::<_, i32>(42) {} for _ in &mut Ok::<_, i32>(42) {} // Should not lint for _ in Some(42).into_iter() {} for _ in Some(42).iter() {} for _ in Some(42).iter_mut() {} for _ in Ok::<_, i32>(42).into_iter() {} for _ in Ok::<_, i32>(42).iter() {} for _ in Ok::<_, i32>(42).iter_mut() {} } ``` <details><summary><code>cargo build</code> diff</summary> ```diff diff --git a/old.out b/new.out index 84215aa..ca195a7 100644 --- a/old.out +++ b/new.out `@@` -1,33 +1,93 `@@` warning: for loop over an `Option`. This is more readably written as an `if let` statement --> src/main.rs:3:14 | 3 | for _ in Some(42) {} | ^^^^^^^^ | = note: `#[warn(for_loops_over_fallibles)]` on by default help: to check pattern in a loop use `while let` | 3 | while let Some(_) = Some(42) {} | ~~~~~~~~~~~~~~~ ~~~ help: consider using `if let` to clear intent | 3 | if let Some(_) = Some(42) {} | ~~~~~~~~~~~~ ~~~ warning: for loop over a `Result`. This is more readably written as an `if let` statement --> src/main.rs:4:14 | 4 | for _ in Ok::<_, i32>(42) {} | ^^^^^^^^^^^^^^^^ | help: to check pattern in a loop use `while let` | 4 | while let Ok(_) = Ok::<_, i32>(42) {} | ~~~~~~~~~~~~~ ~~~ help: consider using `if let` to clear intent | 4 | if let Ok(_) = Ok::<_, i32>(42) {} | ~~~~~~~~~~ ~~~ -warning: `for-loops-over-fallibles` (bin "for-loops-over-fallibles") generated 2 warnings - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.04s +warning: for loop over a `&Option`. This is more readably written as an `if let` statement + --> src/main.rs:7:14 + | +7 | for _ in &Some(42) {} + | ^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +7 | while let Some(_) = &Some(42) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +7 | if let Some(_) = &Some(42) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement + --> src/main.rs:8:14 + | +8 | for _ in &mut Some(42) {} + | ^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +8 | while let Some(_) = &mut Some(42) {} + | ~~~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +8 | if let Some(_) = &mut Some(42) {} + | ~~~~~~~~~~~~ ~~~ + +warning: for loop over a `&Result`. This is more readably written as an `if let` statement + --> src/main.rs:9:14 + | +9 | for _ in &Ok::<_, i32>(42) {} + | ^^^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +9 | while let Ok(_) = &Ok::<_, i32>(42) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +9 | if let Ok(_) = &Ok::<_, i32>(42) {} + | ~~~~~~~~~~ ~~~ + +warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement + --> src/main.rs:10:14 + | +10 | for _ in &mut Ok::<_, i32>(42) {} + | ^^^^^^^^^^^^^^^^^^^^^ + | +help: to check pattern in a loop use `while let` + | +10 | while let Ok(_) = &mut Ok::<_, i32>(42) {} + | ~~~~~~~~~~~~~ ~~~ +help: consider using `if let` to clear intent + | +10 | if let Ok(_) = &mut Ok::<_, i32>(42) {} + | ~~~~~~~~~~ ~~~ + +warning: `for-loops-over-fallibles` (bin "for-loops-over-fallibles") generated 6 warnings + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.02s ``` </details> ----- Question: * ~~Currently, the article `an` is used for `&Option`, and `&mut Option` in the lint diagnostic, since that's what `Option` uses. Is this okay or should it be changed? (likewise, `a` is used for `&Result` and `&mut Result`)~~ The article `a` is used for `&Option`, `&mut Option`, `&Result`, `&mut Result` and (as before) `Result`. Only `Option` uses `an` (as before). `@rustbot` label +A-lint
2 parents 72fd85c + a59d071 commit 4af1c31

File tree

11 files changed

+112
-15
lines changed

11 files changed

+112
-15
lines changed

compiler/rustc_builtin_macros/src/deriving/default.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::deriving::generic::*;
33
use crate::errors;
44
use core::ops::ControlFlow;
55
use rustc_ast as ast;
6-
use rustc_ast::visit::walk_list;
6+
use rustc_ast::visit::visit_opt;
77
use rustc_ast::{attr, EnumDef, VariantData};
88
use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
99
use rustc_span::symbol::Ident;
@@ -224,7 +224,7 @@ impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, '
224224
self.visit_ident(v.ident);
225225
self.visit_vis(&v.vis);
226226
self.visit_variant_data(&v.data);
227-
walk_list!(self, visit_anon_const, &v.disr_expr);
227+
visit_opt!(self, visit_anon_const, &v.disr_expr);
228228
for attr in &v.attrs {
229229
rustc_ast::visit::walk_attribute(self, attr);
230230
}

compiler/rustc_hir_analysis/src/check/region.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
//!
77
//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html
88
9-
use rustc_ast::visit::walk_list;
9+
use rustc_ast::visit::visit_opt;
1010
use rustc_data_structures::fx::FxHashSet;
1111
use rustc_hir as hir;
1212
use rustc_hir::def_id::DefId;
@@ -168,7 +168,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
168168
hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
169169
}
170170
}
171-
walk_list!(visitor, visit_expr, &blk.expr);
171+
visit_opt!(visitor, visit_expr, &blk.expr);
172172
}
173173

174174
visitor.cx = prev_cx;

compiler/rustc_lint/messages.ftl

+1-1
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ lint_extern_without_abi = extern declarations without an explicit ABI are deprec
263263
.help = the default ABI is {$default_abi}
264264
265265
lint_for_loops_over_fallibles =
266-
for loop over {$article} `{$ty}`. This is more readably written as an `if let` statement
266+
for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
267267
.suggestion = consider using `if let` to clear intent
268268
.remove_next = to iterate over `{$recv_snip}` remove the call to `next`
269269
.use_while_let = to check pattern in a loop use `while let`

compiler/rustc_lint/src/for_loops_over_fallibles.rs

+15-2
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,27 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
5252

5353
let ty = cx.typeck_results().expr_ty(arg);
5454

55-
let &ty::Adt(adt, args) = ty.kind() else { return };
55+
let (adt, args, ref_mutability) = match ty.kind() {
56+
&ty::Adt(adt, args) => (adt, args, None),
57+
&ty::Ref(_, ty, mutability) => match ty.kind() {
58+
&ty::Adt(adt, args) => (adt, args, Some(mutability)),
59+
_ => return,
60+
},
61+
_ => return,
62+
};
5663

5764
let (article, ty, var) = match adt.did() {
65+
did if cx.tcx.is_diagnostic_item(sym::Option, did) && ref_mutability.is_some() => ("a", "Option", "Some"),
5866
did if cx.tcx.is_diagnostic_item(sym::Option, did) => ("an", "Option", "Some"),
5967
did if cx.tcx.is_diagnostic_item(sym::Result, did) => ("a", "Result", "Ok"),
6068
_ => return,
6169
};
6270

71+
let ref_prefix = match ref_mutability {
72+
None => "",
73+
Some(ref_mutability) => ref_mutability.ref_prefix_str(),
74+
};
75+
6376
let sub = if let Some(recv) = extract_iterator_next_call(cx, arg)
6477
&& let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span)
6578
{
@@ -85,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles {
8598
cx.emit_span_lint(
8699
FOR_LOOPS_OVER_FALLIBLES,
87100
arg.span,
88-
ForLoopsOverFalliblesDiag { article, ty, sub, question_mark, suggestion },
101+
ForLoopsOverFalliblesDiag { article, ref_prefix, ty, sub, question_mark, suggestion },
89102
);
90103
}
91104
}

compiler/rustc_lint/src/lints.rs

+1
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,7 @@ pub enum PtrNullChecksDiag<'a> {
620620
#[diag(lint_for_loops_over_fallibles)]
621621
pub struct ForLoopsOverFalliblesDiag<'a> {
622622
pub article: &'static str,
623+
pub ref_prefix: &'static str,
623624
pub ty: &'static str,
624625
#[subdiagnostic]
625626
pub sub: ForLoopsOverFalliblesLoopSub<'a>,

compiler/rustc_mir_build/src/build/matches/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
925925
for subpattern in prefix.iter() {
926926
self.visit_primary_bindings(subpattern, pattern_user_ty.clone().index(), f);
927927
}
928-
for subpattern in slice {
928+
if let Some(subpattern) = slice {
929929
self.visit_primary_bindings(
930930
subpattern,
931931
pattern_user_ty.clone().subslice(from, to),

compiler/rustc_resolve/src/late.rs

+4-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use crate::{Module, ModuleOrUniformRoot, NameBinding, ParentScope, PathResult};
1212
use crate::{ResolutionError, Resolver, Segment, UseError};
1313

1414
use rustc_ast::ptr::P;
15-
use rustc_ast::visit::{walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
15+
use rustc_ast::visit::{visit_opt, walk_list, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
1616
use rustc_ast::*;
1717
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
1818
use rustc_errors::{codes::*, Applicability, DiagArgValue, IntoDiagArg, StashKey};
@@ -3280,7 +3280,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
32803280
fn resolve_local(&mut self, local: &'ast Local) {
32813281
debug!("resolving local ({:?})", local);
32823282
// Resolve the type.
3283-
walk_list!(self, visit_ty, &local.ty);
3283+
visit_opt!(self, visit_ty, &local.ty);
32843284

32853285
// Resolve the initializer.
32863286
if let Some((init, els)) = local.kind.init_else_opt() {
@@ -3479,8 +3479,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
34793479
fn resolve_arm(&mut self, arm: &'ast Arm) {
34803480
self.with_rib(ValueNS, RibKind::Normal, |this| {
34813481
this.resolve_pattern_top(&arm.pat, PatternSource::Match);
3482-
walk_list!(this, visit_expr, &arm.guard);
3483-
walk_list!(this, visit_expr, &arm.body);
3482+
visit_opt!(this, visit_expr, &arm.guard);
3483+
visit_opt!(this, visit_expr, &arm.body);
34843484
});
34853485
}
34863486

library/core/tests/result.rs

+1
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ pub fn test_iter() {
170170
}
171171

172172
#[test]
173+
#[allow(for_loops_over_fallibles)]
173174
pub fn test_iter_mut() {
174175
let mut ok: Result<isize, &'static str> = Ok(100);
175176
for loc in ok.iter_mut() {

src/tools/compiletest/src/json.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ fn push_expected_errors(
282282

283283
// Add notes for the backtrace
284284
for span in primary_spans {
285-
for frame in &span.expansion {
285+
if let Some(frame) = &span.expansion {
286286
push_backtrace(expected_errors, frame, file_name);
287287
}
288288
}
@@ -315,7 +315,7 @@ fn push_backtrace(
315315
});
316316
}
317317

318-
for previous_expansion in &expansion.span.expansion {
318+
if let Some(previous_expansion) = &expansion.span.expansion {
319319
push_backtrace(expected_errors, previous_expansion, file_name);
320320
}
321321
}

tests/ui/lint/for_loop_over_fallibles.rs

+22
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,25 @@ fn _returns_result() -> Result<(), ()> {
4141

4242
Ok(())
4343
}
44+
45+
fn _by_ref() {
46+
// Shared refs
47+
for _ in &Some(1) {}
48+
//~^ WARN for loop over a `&Option`. This is more readably written as an `if let` statement
49+
//~| HELP to check pattern in a loop use `while let`
50+
//~| HELP consider using `if let` to clear intent
51+
for _ in &Ok::<_, ()>(1) {}
52+
//~^ WARN for loop over a `&Result`. This is more readably written as an `if let` statement
53+
//~| HELP to check pattern in a loop use `while let`
54+
//~| HELP consider using `if let` to clear intent
55+
56+
// Mutable refs
57+
for _ in &mut Some(1) {}
58+
//~^ WARN for loop over a `&mut Option`. This is more readably written as an `if let` statement
59+
//~| HELP to check pattern in a loop use `while let`
60+
//~| HELP consider using `if let` to clear intent
61+
for _ in &mut Ok::<_, ()>(1) {}
62+
//~^ WARN for loop over a `&mut Result`. This is more readably written as an `if let` statement
63+
//~| HELP to check pattern in a loop use `while let`
64+
//~| HELP consider using `if let` to clear intent
65+
}

tests/ui/lint/for_loop_over_fallibles.stderr

+61-1
Original file line numberDiff line numberDiff line change
@@ -97,5 +97,65 @@ help: consider using `if let` to clear intent
9797
LL | if let Ok(_) = Ok::<_, ()>([0; 0]) {}
9898
| ~~~~~~~~~~ ~~~
9999

100-
warning: 6 warnings emitted
100+
warning: for loop over a `&Option`. This is more readably written as an `if let` statement
101+
--> $DIR/for_loop_over_fallibles.rs:47:14
102+
|
103+
LL | for _ in &Some(1) {}
104+
| ^^^^^^^^
105+
|
106+
help: to check pattern in a loop use `while let`
107+
|
108+
LL | while let Some(_) = &Some(1) {}
109+
| ~~~~~~~~~~~~~~~ ~~~
110+
help: consider using `if let` to clear intent
111+
|
112+
LL | if let Some(_) = &Some(1) {}
113+
| ~~~~~~~~~~~~ ~~~
114+
115+
warning: for loop over a `&Result`. This is more readably written as an `if let` statement
116+
--> $DIR/for_loop_over_fallibles.rs:51:14
117+
|
118+
LL | for _ in &Ok::<_, ()>(1) {}
119+
| ^^^^^^^^^^^^^^^
120+
|
121+
help: to check pattern in a loop use `while let`
122+
|
123+
LL | while let Ok(_) = &Ok::<_, ()>(1) {}
124+
| ~~~~~~~~~~~~~ ~~~
125+
help: consider using `if let` to clear intent
126+
|
127+
LL | if let Ok(_) = &Ok::<_, ()>(1) {}
128+
| ~~~~~~~~~~ ~~~
129+
130+
warning: for loop over a `&mut Option`. This is more readably written as an `if let` statement
131+
--> $DIR/for_loop_over_fallibles.rs:57:14
132+
|
133+
LL | for _ in &mut Some(1) {}
134+
| ^^^^^^^^^^^^
135+
|
136+
help: to check pattern in a loop use `while let`
137+
|
138+
LL | while let Some(_) = &mut Some(1) {}
139+
| ~~~~~~~~~~~~~~~ ~~~
140+
help: consider using `if let` to clear intent
141+
|
142+
LL | if let Some(_) = &mut Some(1) {}
143+
| ~~~~~~~~~~~~ ~~~
144+
145+
warning: for loop over a `&mut Result`. This is more readably written as an `if let` statement
146+
--> $DIR/for_loop_over_fallibles.rs:61:14
147+
|
148+
LL | for _ in &mut Ok::<_, ()>(1) {}
149+
| ^^^^^^^^^^^^^^^^^^^
150+
|
151+
help: to check pattern in a loop use `while let`
152+
|
153+
LL | while let Ok(_) = &mut Ok::<_, ()>(1) {}
154+
| ~~~~~~~~~~~~~ ~~~
155+
help: consider using `if let` to clear intent
156+
|
157+
LL | if let Ok(_) = &mut Ok::<_, ()>(1) {}
158+
| ~~~~~~~~~~ ~~~
159+
160+
warning: 10 warnings emitted
101161

0 commit comments

Comments
 (0)