rust/tests/ui/range_plus_minus_one.fixed
Samuel Tardieu 15fc993775
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.
2025-06-18 06:41:40 +02:00

182 lines
3.9 KiB
Rust

#![warn(clippy::range_minus_one, clippy::range_plus_one)]
#![allow(unused_parens)]
#![allow(clippy::iter_with_drain)]
use std::ops::{Index, IndexMut, Range, RangeBounds, RangeInclusive};
fn f() -> usize {
42
}
macro_rules! macro_plus_one {
($m: literal) => {
for i in 0..$m + 1 {
println!("{}", i);
}
};
}
macro_rules! macro_minus_one {
($m: literal) => {
for i in 0..=$m - 1 {
println!("{}", i);
}
};
}
fn main() {
for _ in 0..2 {}
for _ in 0..=2 {}
for _ in 0..=3 {}
//~^ range_plus_one
for _ in 0..=3 + 1 {}
for _ in 0..=5 {}
//~^ range_plus_one
for _ in 0..=1 + 5 {}
for _ in 1..=1 {}
//~^ range_plus_one
for _ in 1..=1 + 1 {}
for _ in 0..13 + 13 {}
for _ in 0..=13 - 7 {}
for _ in 0..=f() {}
//~^ range_plus_one
for _ in 0..=(1 + f()) {}
// Those are not linted, as in the general case we cannot be sure that the exact type won't be
// important.
let _ = ..11 - 1;
let _ = ..=11 - 1;
let _ = ..=(11 - 1);
let _ = (1..11 + 1);
let _ = (f() + 1)..(f() + 1);
const ONE: usize = 1;
// integer consts are linted, too
for _ in 1..=ONE {}
//~^ range_plus_one
let mut vec: Vec<()> = std::vec::Vec::new();
vec.drain(..);
macro_plus_one!(5);
macro_minus_one!(5);
// As an instance of `Iterator`
(1..=10).for_each(|_| {});
//~^ range_plus_one
// As an instance of `IntoIterator`
#[allow(clippy::useless_conversion)]
(1..=10).into_iter().for_each(|_| {});
//~^ range_plus_one
// As an instance of `RangeBounds`
{
let _ = (1..=10).start_bound();
//~^ range_plus_one
}
// As a `SliceIndex`
let a = [10, 20, 30];
let _ = &a[1..=1];
//~^ range_plus_one
// As method call argument
vec.drain(2..=3);
//~^ range_plus_one
// As function call argument
take_arg(10..=20);
//~^ range_plus_one
// As function call argument inside a block
take_arg({ 10..=20 });
//~^ range_plus_one
// Do not lint in case types are unified
take_arg(if true { 10..20 } else { 10..20 + 1 });
// Do not lint, as the same type is used for both parameters
take_args(10..20 + 1, 10..21);
// Do not lint, as the range type is also used indirectly in second parameter
take_arg_and_struct(10..20 + 1, S { t: 1..2 });
// As target of `IndexMut`
let mut a = [10, 20, 30];
a[0..=2][0] = 1;
//~^ range_plus_one
}
fn take_arg<T: Iterator<Item = u32>>(_: T) {}
fn take_args<T: Iterator<Item = u32>>(_: T, _: T) {}
struct S<T> {
t: T,
}
fn take_arg_and_struct<T: Iterator<Item = u32>>(_: T, _: S<T>) {}
fn no_index_by_range_inclusive(a: usize) {
struct S;
impl Index<Range<usize>> for S {
type Output = [u32];
fn index(&self, _: Range<usize>) -> &Self::Output {
&[]
}
}
_ = &S[0..a + 1];
}
fn no_index_mut_with_switched_range(a: usize) {
struct S(u32);
impl Index<Range<usize>> for S {
type Output = u32;
fn index(&self, _: Range<usize>) -> &Self::Output {
&self.0
}
}
impl IndexMut<Range<usize>> for S {
fn index_mut(&mut self, _: Range<usize>) -> &mut Self::Output {
&mut self.0
}
}
impl Index<RangeInclusive<usize>> for S {
type Output = u32;
fn index(&self, _: RangeInclusive<usize>) -> &Self::Output {
&self.0
}
}
S(2)[0..a + 1] = 3;
}
fn issue9908() {
// Simplified test case
let _ = || 0..=1;
// Original test case
let full_length = 1024;
let range = {
// do some stuff, omit here
None
};
let range = range.map(|(s, t)| s..=t).unwrap_or(0..=(full_length - 1));
assert_eq!(range, 0..=1023);
}
fn issue9908_2(n: usize) -> usize {
(1..n).sum()
//~^ range_minus_one
}