Skip to content

Commit 443ee08

Browse files
committed
Refactor range_plus_one and range_minus_one lints
Those lints share the detection logic structure, but differed, probably because of an oversight, in lint emission: only one of them would take care of emitting parentheses around the suggestion if required. Factor the code into one function which checks for the right condition and emits the lint in an identical way.
1 parent f532213 commit 443ee08

File tree

4 files changed

+121
-72
lines changed

4 files changed

+121
-72
lines changed

Diff for: clippy_lints/src/ranges.rs

+56-56
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use clippy_utils::{
1010
use rustc_ast::ast::RangeLimits;
1111
use rustc_errors::Applicability;
1212
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, LangItem};
13-
use rustc_lint::{LateContext, LateLintPass};
13+
use rustc_lint::{LateContext, LateLintPass, Lint};
1414
use rustc_middle::ty::{self, ClauseKind, PredicatePolarity};
1515
use rustc_session::impl_lint_pass;
1616
use rustc_span::source_map::Spanned;
@@ -430,71 +430,71 @@ fn can_switch_ranges(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
430430
}
431431

432432
// exclusive range plus one: `x..(y+1)`
433-
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
434-
if expr.span.can_be_used_for_suggestions()
435-
&& let Some(higher::Range {
436-
start,
437-
end: Some(end),
438-
limits: RangeLimits::HalfOpen,
439-
}) = higher::Range::hir(expr)
440-
&& let Some(y) = y_plus_one(cx, end)
441-
&& can_switch_ranges(cx, expr)
442-
{
443-
let span = expr.span;
444-
span_lint_and_then(
445-
cx,
446-
RANGE_PLUS_ONE,
447-
span,
448-
"an inclusive range would be more readable",
449-
|diag| {
450-
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string());
451-
let end = Sugg::hir(cx, y, "y").maybe_paren();
452-
match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) {
453-
Some(true) => {
454-
diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect);
455-
},
456-
Some(false) => {
457-
diag.span_suggestion(
458-
span,
459-
"use",
460-
format!("{start}..={end}"),
461-
Applicability::MachineApplicable, // snippet
462-
);
463-
},
464-
None => {},
465-
}
466-
},
467-
);
468-
}
433+
fn check_exclusive_range_plus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
434+
check_range_switch(
435+
cx,
436+
expr,
437+
RangeLimits::HalfOpen,
438+
y_plus_one,
439+
RANGE_PLUS_ONE,
440+
"an inclusive range would be more readable",
441+
"..=",
442+
);
469443
}
470444

471445
// inclusive range minus one: `x..=(y-1)`
472-
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
446+
fn check_inclusive_range_minus_one<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
447+
check_range_switch(
448+
cx,
449+
expr,
450+
RangeLimits::Closed,
451+
y_minus_one,
452+
RANGE_MINUS_ONE,
453+
"an exclusive range would be more readable",
454+
"..",
455+
);
456+
}
457+
458+
/// Check for a `kind` of range in `expr`, check for `predicate` on the end,
459+
/// and emit the `lint` with `msg` and the `operator`.
460+
fn check_range_switch<'tcx>(
461+
cx: &LateContext<'tcx>,
462+
expr: &'tcx Expr<'tcx>,
463+
kind: RangeLimits,
464+
predicate: impl for<'lt> FnOnce(&LateContext<'lt>, &'lt Expr<'lt>) -> Option<&'lt Expr<'lt>>,
465+
lint: &'static Lint,
466+
msg: &'static str,
467+
operator: &str,
468+
) {
473469
if expr.span.can_be_used_for_suggestions()
474470
&& let Some(higher::Range {
475471
start,
476472
end: Some(end),
477-
limits: RangeLimits::Closed,
473+
limits,
478474
}) = higher::Range::hir(expr)
479-
&& let Some(y) = y_minus_one(cx, end)
475+
&& limits == kind
476+
&& let Some(y) = predicate(cx, end)
480477
&& can_switch_ranges(cx, expr)
481478
{
482-
span_lint_and_then(
483-
cx,
484-
RANGE_MINUS_ONE,
485-
expr.span,
486-
"an exclusive range would be more readable",
487-
|diag| {
488-
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string());
489-
let end = Sugg::hir(cx, y, "y").maybe_paren();
490-
diag.span_suggestion(
491-
expr.span,
492-
"use",
493-
format!("{start}..{end}"),
494-
Applicability::MachineApplicable, // snippet
495-
);
496-
},
497-
);
479+
let span = expr.span;
480+
span_lint_and_then(cx, lint, span, msg, |diag| {
481+
let mut app = Applicability::MachineApplicable;
482+
let start = start.map_or(String::new(), |x| {
483+
Sugg::hir_with_applicability(cx, x, "<x>", &mut app)
484+
.maybe_paren()
485+
.to_string()
486+
});
487+
let end = Sugg::hir_with_applicability(cx, y, "<y>", &mut app).maybe_paren();
488+
match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) {
489+
Some(true) => {
490+
diag.span_suggestion(span, "use", format!("({start}{operator}{end})"), app);
491+
},
492+
Some(false) => {
493+
diag.span_suggestion(span, "use", format!("{start}{operator}{end}"), app);
494+
},
495+
None => {},
496+
}
497+
});
498498
}
499499
}
500500

Diff for: tests/ui/range_plus_minus_one.fixed

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![warn(clippy::range_minus_one, clippy::range_plus_one)]
12
#![allow(unused_parens)]
23
#![allow(clippy::iter_with_drain)]
34
fn f() -> usize {
@@ -20,8 +21,6 @@ macro_rules! macro_minus_one {
2021
};
2122
}
2223

23-
#[warn(clippy::range_plus_one)]
24-
#[warn(clippy::range_minus_one)]
2524
fn main() {
2625
for _ in 0..2 {}
2726
for _ in 0..=2 {}
@@ -99,3 +98,24 @@ fn main() {
9998

10099
fn take_arg<T: Iterator<Item = u32>>(_: T) {}
101100
fn take_args<T: Iterator<Item = u32>>(_: T, _: T) {}
101+
102+
fn issue9908() {
103+
// Simplified test case
104+
let _ = || 0..=1;
105+
106+
// Original test case
107+
let full_length = 1024;
108+
let range = {
109+
// do some stuff, omit here
110+
None
111+
};
112+
113+
let range = range.map(|(s, t)| s..=t).unwrap_or(0..=(full_length - 1));
114+
115+
assert_eq!(range, 0..=1023);
116+
}
117+
118+
fn issue9908_2(n: usize) -> usize {
119+
(1..n).sum()
120+
//~^ range_minus_one
121+
}

Diff for: tests/ui/range_plus_minus_one.rs

+22-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#![warn(clippy::range_minus_one, clippy::range_plus_one)]
12
#![allow(unused_parens)]
23
#![allow(clippy::iter_with_drain)]
34
fn f() -> usize {
@@ -20,8 +21,6 @@ macro_rules! macro_minus_one {
2021
};
2122
}
2223

23-
#[warn(clippy::range_plus_one)]
24-
#[warn(clippy::range_minus_one)]
2524
fn main() {
2625
for _ in 0..2 {}
2726
for _ in 0..=2 {}
@@ -99,3 +98,24 @@ fn main() {
9998

10099
fn take_arg<T: Iterator<Item = u32>>(_: T) {}
101100
fn take_args<T: Iterator<Item = u32>>(_: T, _: T) {}
101+
102+
fn issue9908() {
103+
// Simplified test case
104+
let _ = || 0..=1;
105+
106+
// Original test case
107+
let full_length = 1024;
108+
let range = {
109+
// do some stuff, omit here
110+
None
111+
};
112+
113+
let range = range.map(|(s, t)| s..=t).unwrap_or(0..=(full_length - 1));
114+
115+
assert_eq!(range, 0..=1023);
116+
}
117+
118+
fn issue9908_2(n: usize) -> usize {
119+
(1..=n - 1).sum()
120+
//~^ range_minus_one
121+
}

Diff for: tests/ui/range_plus_minus_one.stderr

+21-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: an inclusive range would be more readable
2-
--> tests/ui/range_plus_minus_one.rs:29:14
2+
--> tests/ui/range_plus_minus_one.rs:28:14
33
|
44
LL | for _ in 0..3 + 1 {}
55
| ^^^^^^^^ help: use: `0..=3`
@@ -8,64 +8,73 @@ LL | for _ in 0..3 + 1 {}
88
= help: to override `-D warnings` add `#[allow(clippy::range_plus_one)]`
99

1010
error: an inclusive range would be more readable
11-
--> tests/ui/range_plus_minus_one.rs:33:14
11+
--> tests/ui/range_plus_minus_one.rs:32:14
1212
|
1313
LL | for _ in 0..1 + 5 {}
1414
| ^^^^^^^^ help: use: `0..=5`
1515

1616
error: an inclusive range would be more readable
17-
--> tests/ui/range_plus_minus_one.rs:37:14
17+
--> tests/ui/range_plus_minus_one.rs:36:14
1818
|
1919
LL | for _ in 1..1 + 1 {}
2020
| ^^^^^^^^ help: use: `1..=1`
2121

2222
error: an inclusive range would be more readable
23-
--> tests/ui/range_plus_minus_one.rs:44:14
23+
--> tests/ui/range_plus_minus_one.rs:43:14
2424
|
2525
LL | for _ in 0..(1 + f()) {}
2626
| ^^^^^^^^^^^^ help: use: `0..=f()`
2727

2828
error: an inclusive range would be more readable
29-
--> tests/ui/range_plus_minus_one.rs:58:14
29+
--> tests/ui/range_plus_minus_one.rs:57:14
3030
|
3131
LL | for _ in 1..ONE + ONE {}
3232
| ^^^^^^^^^^^^ help: use: `1..=ONE`
3333

3434
error: an inclusive range would be more readable
35-
--> tests/ui/range_plus_minus_one.rs:68:5
35+
--> tests/ui/range_plus_minus_one.rs:67:5
3636
|
3737
LL | (1..10 + 1).for_each(|_| {});
3838
| ^^^^^^^^^^^ help: use: `(1..=10)`
3939

4040
error: an inclusive range would be more readable
41-
--> tests/ui/range_plus_minus_one.rs:73:5
41+
--> tests/ui/range_plus_minus_one.rs:72:5
4242
|
4343
LL | (1..10 + 1).into_iter().for_each(|_| {});
4444
| ^^^^^^^^^^^ help: use: `(1..=10)`
4545

4646
error: an inclusive range would be more readable
47-
--> tests/ui/range_plus_minus_one.rs:79:17
47+
--> tests/ui/range_plus_minus_one.rs:78:17
4848
|
4949
LL | let _ = (1..10 + 1).start_bound();
5050
| ^^^^^^^^^^^ help: use: `(1..=10)`
5151

5252
error: an inclusive range would be more readable
53-
--> tests/ui/range_plus_minus_one.rs:85:16
53+
--> tests/ui/range_plus_minus_one.rs:84:16
5454
|
5555
LL | let _ = &a[1..1 + 1];
5656
| ^^^^^^^^ help: use: `1..=1`
5757

5858
error: an inclusive range would be more readable
59-
--> tests/ui/range_plus_minus_one.rs:89:15
59+
--> tests/ui/range_plus_minus_one.rs:88:15
6060
|
6161
LL | vec.drain(2..3 + 1);
6262
| ^^^^^^^^ help: use: `2..=3`
6363

6464
error: an inclusive range would be more readable
65-
--> tests/ui/range_plus_minus_one.rs:93:14
65+
--> tests/ui/range_plus_minus_one.rs:92:14
6666
|
6767
LL | take_arg(10..20 + 1);
6868
| ^^^^^^^^^^ help: use: `10..=20`
6969

70-
error: aborting due to 11 previous errors
70+
error: an exclusive range would be more readable
71+
--> tests/ui/range_plus_minus_one.rs:119:5
72+
|
73+
LL | (1..=n - 1).sum()
74+
| ^^^^^^^^^^^ help: use: `(1..n)`
75+
|
76+
= note: `-D clippy::range-minus-one` implied by `-D warnings`
77+
= help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]`
78+
79+
error: aborting due to 12 previous errors
7180

0 commit comments

Comments
 (0)