Skip to content

Commit 474517e

Browse files
committed
Add empty_iterator_range lint
see #100635
1 parent 02cd79a commit 474517e

File tree

3 files changed

+89
-0
lines changed

3 files changed

+89
-0
lines changed

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ lint_enum_intrinsics_mem_variant =
1313
the return value of `mem::variant_count` is unspecified when called with a non-enum type
1414
.note = the type parameter of `variant_count` should be an enum, but it was instantiated with the type `{$ty_param}`, which is not an enum.
1515
16+
lint_empty_iter_ranges = this `for` loop is never run
17+
.clarification = this loop is never run because it's iterating over an empty iterator
18+
.note = ranges that have a bigger start than their end will produce an empty iterator
19+
.help = if you want a decreasing range sequence, create an increasing range and call `.rev()` on it
20+
1621
lint_expectation = this lint expectation is unfulfilled
1722
.note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message
1823
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
use crate::{LateContext, LateLintPass, LintContext};
2+
3+
use rustc_ast::ast::LitKind;
4+
use rustc_errors::fluent;
5+
use rustc_hir::{Expr, ExprField, ExprKind, MatchSource, PatKind, StmtKind};
6+
use rustc_span::Span;
7+
8+
declare_lint! {
9+
/// The `empty_iterator_range` lint checks for empty iterator ranges.
10+
///
11+
/// ### Example
12+
///
13+
/// ```rust
14+
/// for i in 10..0 { /* ... */ }
15+
/// ```
16+
///
17+
/// {{produces}}
18+
///
19+
/// ### Explanation
20+
///
21+
/// It is usually a mistake to have a statement that has no effect.
22+
pub EMPTY_ITERATOR_RANGE,
23+
Warn,
24+
"empty iterator range"
25+
}
26+
27+
declare_lint_pass!(EmptyIteratorRange => [EMPTY_ITERATOR_RANGE]);
28+
29+
impl<'tcx> LateLintPass<'tcx> for EmptyIteratorRange {
30+
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
31+
let Some(for_expr) = extract_for_loop(expr) else { return };
32+
let span = expr.span.with_hi(for_expr.span.hi());
33+
if let ExprKind::Struct(_, expr_fields, _) = &for_expr.kind {
34+
is_empty_range(cx, span, expr_fields);
35+
} else if let ExprKind::MethodCall(_, expr, _, _) = &for_expr.kind {
36+
if let ExprKind::Struct(_, expr_fields, _) = &expr.kind {
37+
is_empty_range(cx, span, expr_fields);
38+
}
39+
}
40+
}
41+
}
42+
43+
fn extract_for_loop<'tcx>(expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
44+
if let ExprKind::DropTemps(e) = expr.kind
45+
&& let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind
46+
&& let ExprKind::Call(_, [arg]) = iterexpr.kind
47+
&& let ExprKind::Loop(block, ..) = arm.body.kind
48+
&& let [stmt] = block.stmts
49+
&& let StmtKind::Expr(e) = stmt.kind
50+
&& let ExprKind::Match(_, [_, some_arm], _) = e.kind
51+
&& let PatKind::Struct(..) = some_arm.pat.kind
52+
{
53+
Some(arg)
54+
} else {
55+
None
56+
}
57+
}
58+
59+
fn is_empty_range(cx: &LateContext<'_>, span: Span, expr_fields: &[ExprField<'_>]) {
60+
let mut prev = 0u128;
61+
for (index, expr_field) in expr_fields.iter().enumerate() {
62+
if let ExprKind::Lit(lit) = &expr_field.expr.kind {
63+
if let LitKind::Int(u, _) = lit.node {
64+
if index == 0 {
65+
prev = u;
66+
} else if prev > u {
67+
cx.struct_span_lint(
68+
EMPTY_ITERATOR_RANGE,
69+
span,
70+
fluent::lint::empty_iter_ranges,
71+
|lint| {
72+
lint.span_label(span, fluent::lint::clarification)
73+
.note(fluent::lint::note)
74+
.help(fluent::lint::help)
75+
},
76+
);
77+
}
78+
}
79+
}
80+
}
81+
}

compiler/rustc_lint/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ mod array_into_iter;
4949
pub mod builtin;
5050
mod context;
5151
mod early;
52+
mod empty_iterator;
5253
mod enum_intrinsics_non_enums;
5354
mod errors;
5455
mod expect;
@@ -85,6 +86,7 @@ use rustc_span::Span;
8586

8687
use array_into_iter::ArrayIntoIter;
8788
use builtin::*;
89+
use empty_iterator::EmptyIteratorRange;
8890
use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums;
8991
use hidden_unicode_codepoints::*;
9092
use internal::*;
@@ -226,6 +228,7 @@ macro_rules! late_lint_mod_passes {
226228
InvalidAtomicOrdering: InvalidAtomicOrdering,
227229
NamedAsmLabels: NamedAsmLabels,
228230
OpaqueHiddenInferredBound: OpaqueHiddenInferredBound,
231+
EmptyIteratorRange: EmptyIteratorRange,
229232
]
230233
);
231234
};

0 commit comments

Comments
 (0)