Skip to content

Commit 2044877

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 fa2f840 commit 2044877

File tree

4 files changed

+123
-74
lines changed

4 files changed

+123
-74
lines changed

clippy_lints/src/ranges.rs

Lines changed: 57 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_ast::Mutability;
1212
use rustc_ast::ast::RangeLimits;
1313
use rustc_errors::Applicability;
1414
use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, LangItem};
15-
use rustc_lint::{LateContext, LateLintPass};
15+
use rustc_lint::{LateContext, LateLintPass, Lint};
1616
use rustc_middle::ty::{self, ClauseKind, GenericArgKind, PredicatePolarity, Ty};
1717
use rustc_session::impl_lint_pass;
1818
use rustc_span::source_map::Spanned;
@@ -460,70 +460,70 @@ fn can_switch_ranges<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, original: Ra
460460

461461
// exclusive range plus one: `x..(y+1)`
462462
fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
463-
if expr.span.can_be_used_for_suggestions()
464-
&& let Some(higher::Range {
465-
start,
466-
end: Some(end),
467-
limits: RangeLimits::HalfOpen,
468-
}) = higher::Range::hir(expr)
469-
&& let Some(y) = y_plus_one(cx, end)
470-
&& can_switch_ranges(cx, expr, RangeLimits::HalfOpen, cx.typeck_results().expr_ty(y))
471-
{
472-
let span = expr.span;
473-
span_lint_and_then(
474-
cx,
475-
RANGE_PLUS_ONE,
476-
span,
477-
"an inclusive range would be more readable",
478-
|diag| {
479-
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string());
480-
let end = Sugg::hir(cx, y, "y").maybe_paren();
481-
match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) {
482-
Some(true) => {
483-
diag.span_suggestion(span, "use", format!("({start}..={end})"), Applicability::MaybeIncorrect);
484-
},
485-
Some(false) => {
486-
diag.span_suggestion(
487-
span,
488-
"use",
489-
format!("{start}..={end}"),
490-
Applicability::MachineApplicable, // snippet
491-
);
492-
},
493-
None => {},
494-
}
495-
},
496-
);
497-
}
463+
check_range_switch(
464+
cx,
465+
expr,
466+
RangeLimits::HalfOpen,
467+
y_plus_one,
468+
RANGE_PLUS_ONE,
469+
"an inclusive range would be more readable",
470+
"..=",
471+
);
498472
}
499473

500474
// inclusive range minus one: `x..=(y-1)`
501475
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
476+
check_range_switch(
477+
cx,
478+
expr,
479+
RangeLimits::Closed,
480+
y_minus_one,
481+
RANGE_MINUS_ONE,
482+
"an exclusive range would be more readable",
483+
"..",
484+
);
485+
}
486+
487+
/// Check for a `kind` of range in `expr`, check for `predicate` on the end,
488+
/// and emit the `lint` with `msg` and the `operator`.
489+
fn check_range_switch(
490+
cx: &LateContext<'_>,
491+
expr: &Expr<'_>,
492+
kind: RangeLimits,
493+
predicate: impl for<'tcx> FnOnce(&LateContext<'_>, &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>>,
494+
lint: &'static Lint,
495+
msg: &'static str,
496+
operator: &str,
497+
) {
502498
if expr.span.can_be_used_for_suggestions()
503499
&& let Some(higher::Range {
504500
start,
505501
end: Some(end),
506-
limits: RangeLimits::Closed,
502+
limits,
507503
}) = higher::Range::hir(expr)
508-
&& let Some(y) = y_minus_one(cx, end)
509-
&& can_switch_ranges(cx, expr, RangeLimits::Closed, cx.typeck_results().expr_ty(y))
504+
&& limits == kind
505+
&& let Some(y) = predicate(cx, end)
506+
&& can_switch_ranges(cx, expr, kind, cx.typeck_results().expr_ty(y))
510507
{
511-
span_lint_and_then(
512-
cx,
513-
RANGE_MINUS_ONE,
514-
expr.span,
515-
"an exclusive range would be more readable",
516-
|diag| {
517-
let start = start.map_or(String::new(), |x| Sugg::hir(cx, x, "x").maybe_paren().to_string());
518-
let end = Sugg::hir(cx, y, "y").maybe_paren();
519-
diag.span_suggestion(
520-
expr.span,
521-
"use",
522-
format!("{start}..{end}"),
523-
Applicability::MachineApplicable, // snippet
524-
);
525-
},
526-
);
508+
let span = expr.span;
509+
span_lint_and_then(cx, lint, span, msg, |diag| {
510+
let mut app = Applicability::MachineApplicable;
511+
let start = start.map_or(String::new(), |x| {
512+
Sugg::hir_with_applicability(cx, x, "<x>", &mut app)
513+
.maybe_paren()
514+
.to_string()
515+
});
516+
let end = Sugg::hir_with_applicability(cx, y, "<y>", &mut app).maybe_paren();
517+
match span.with_source_text(cx, |src| src.starts_with('(') && src.ends_with(')')) {
518+
Some(true) => {
519+
diag.span_suggestion(span, "use", format!("({start}{operator}{end})"), app);
520+
},
521+
Some(false) => {
522+
diag.span_suggestion(span, "use", format!("{start}{operator}{end}"), app);
523+
},
524+
None => {},
525+
}
526+
});
527527
}
528528
}
529529

@@ -610,7 +610,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
610610
}
611611
}
612612

613-
fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
613+
fn y_plus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
614614
match expr.kind {
615615
ExprKind::Binary(
616616
Spanned {
@@ -631,7 +631,7 @@ fn y_plus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'
631631
}
632632
}
633633

634-
fn y_minus_one<'t>(cx: &LateContext<'_>, expr: &'t Expr<'_>) -> Option<&'t Expr<'t>> {
634+
fn y_minus_one<'tcx>(cx: &LateContext<'_>, expr: &Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
635635
match expr.kind {
636636
ExprKind::Binary(
637637
Spanned {

tests/ui/range_plus_minus_one.fixed

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#![warn(clippy::range_minus_one)]
2-
#![warn(clippy::range_plus_one)]
1+
#![warn(clippy::range_minus_one, clippy::range_plus_one)]
32
#![allow(unused_parens)]
43
#![allow(clippy::iter_with_drain)]
54

@@ -153,3 +152,24 @@ fn no_index_mut_with_switched_range(a: usize) {
153152

154153
S(2)[0..a + 1] = 3;
155154
}
155+
156+
fn issue9908() {
157+
// Simplified test case
158+
let _ = || 0..=1;
159+
160+
// Original test case
161+
let full_length = 1024;
162+
let range = {
163+
// do some stuff, omit here
164+
None
165+
};
166+
167+
let range = range.map(|(s, t)| s..=t).unwrap_or(0..=(full_length - 1));
168+
169+
assert_eq!(range, 0..=1023);
170+
}
171+
172+
fn issue9908_2(n: usize) -> usize {
173+
(1..n).sum()
174+
//~^ range_minus_one
175+
}

tests/ui/range_plus_minus_one.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
#![warn(clippy::range_minus_one)]
2-
#![warn(clippy::range_plus_one)]
1+
#![warn(clippy::range_minus_one, clippy::range_plus_one)]
32
#![allow(unused_parens)]
43
#![allow(clippy::iter_with_drain)]
54

@@ -153,3 +152,24 @@ fn no_index_mut_with_switched_range(a: usize) {
153152

154153
S(2)[0..a + 1] = 3;
155154
}
155+
156+
fn issue9908() {
157+
// Simplified test case
158+
let _ = || 0..=1;
159+
160+
// Original test case
161+
let full_length = 1024;
162+
let range = {
163+
// do some stuff, omit here
164+
None
165+
};
166+
167+
let range = range.map(|(s, t)| s..=t).unwrap_or(0..=(full_length - 1));
168+
169+
assert_eq!(range, 0..=1023);
170+
}
171+
172+
fn issue9908_2(n: usize) -> usize {
173+
(1..=n - 1).sum()
174+
//~^ range_minus_one
175+
}

tests/ui/range_plus_minus_one.stderr

Lines changed: 22 additions & 13 deletions
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:32:14
2+
--> tests/ui/range_plus_minus_one.rs:31:14
33
|
44
LL | for _ in 0..3 + 1 {}
55
| ^^^^^^^^ help: use: `0..=3`
@@ -8,70 +8,79 @@ 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:36:14
11+
--> tests/ui/range_plus_minus_one.rs:35: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:40:14
17+
--> tests/ui/range_plus_minus_one.rs:39: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:47:14
23+
--> tests/ui/range_plus_minus_one.rs:46: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:61:14
29+
--> tests/ui/range_plus_minus_one.rs:60: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:71:5
35+
--> tests/ui/range_plus_minus_one.rs:70: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:76:5
41+
--> tests/ui/range_plus_minus_one.rs:75: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:81:17
47+
--> tests/ui/range_plus_minus_one.rs:80: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:87:16
53+
--> tests/ui/range_plus_minus_one.rs:86: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:91:15
59+
--> tests/ui/range_plus_minus_one.rs:90: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:95:14
65+
--> tests/ui/range_plus_minus_one.rs:94:14
6666
|
6767
LL | take_arg(10..20 + 1);
6868
| ^^^^^^^^^^ help: use: `10..=20`
6969

7070
error: an inclusive range would be more readable
71-
--> tests/ui/range_plus_minus_one.rs:106:7
71+
--> tests/ui/range_plus_minus_one.rs:105:7
7272
|
7373
LL | a[0..2 + 1][0] = 1;
7474
| ^^^^^^^^ help: use: `0..=2`
7575

76-
error: aborting due to 12 previous errors
76+
error: an exclusive range would be more readable
77+
--> tests/ui/range_plus_minus_one.rs:173:5
78+
|
79+
LL | (1..=n - 1).sum()
80+
| ^^^^^^^^^^^ help: use: `(1..n)`
81+
|
82+
= note: `-D clippy::range-minus-one` implied by `-D warnings`
83+
= help: to override `-D warnings` add `#[allow(clippy::range_minus_one)]`
84+
85+
error: aborting due to 13 previous errors
7786

0 commit comments

Comments
 (0)