Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

range_plus_one lint wrongly suggests using RangeInclusive when Range is required and the other way around #3307

Open
asomers opened this issue Oct 12, 2018 · 13 comments
Labels
C-bug Category: Clippy is not doing the correct thing I-suggestion-causes-error Issue: The suggestions provided by this Lint cause an ICE/error when applied L-suggestion Lint: Improving, adding or fixing lint suggestions

Comments

@asomers
Copy link

asomers commented Oct 12, 2018

The range_plus_one lint suggests replacing Range syntax with RangeInclusive syntax. That's fine if you're immediately going to loop. But it's invalid if the Range in question is going to be assigned to a variable of type Range. For example, clippy will suggest replacing this code:

struct Foo {
    r: Range<u32>
}

fn bar() {
    let x = 0;
    let foo = Foo{r: x..x+1};
}

with this:

struct Foo {
    r: Range<u32>
}

fn bar() {
    let x = 0;
    let foo = Foo{r: x..=x};
}

But that fails to compile

2263 |     let foo = Foo{r: x..=x};
     |                      ^^^^^ expected struct `std::ops::Range`, found struct `std::ops::RangeInclusive`                                                                
     |
     = note: expected type `std::ops::Range<u32>`
                found type `std::ops::RangeInclusive<{integer}>`
clippy 0.0.212 (32b1d1fc 2018-10-05)
@jonasbb
Copy link

jonasbb commented Dec 6, 2018

Another example using rayon:

The following program compiles, but clippy produces a warning on it:

extern crate rayon;
use rayon::prelude::*;
fn main() {
    (0..1 + 1).into_par_iter();
}

The warning is:

warning: an inclusive range would be more readable
 --> src/main.rs:4:5
  |
4 |     (0..1 + 1).into_par_iter();
  |     ^^^^^^^^^^ help: use: `(0..=1)`
  |
  = note: #[warn(clippy::range_plus_one)] on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#range_plus_one

However, applying the suggestion results in the following error:

error[E0599]: no method named `into_par_iter` found for type `std::ops::RangeInclusive<{integer}>` in the current scope
 --> src/main.rs:4:13
  |
4 |     (0..=1).into_par_iter();
  |             ^^^^^^^^^^^^^
  |
  = note: the method `into_par_iter` exists but the following trait bounds were not satisfied:
          `std::ops::RangeInclusive<{integer}> : rayon::iter::IntoParallelIterator`
          `&std::ops::RangeInclusive<{integer}> : rayon::iter::IntoParallelIterator`
          `&mut std::ops::RangeInclusive<{integer}> : rayon::iter::IntoParallelIterator`

See also the rayon documentation for ranges.

@flip1995 flip1995 added C-bug Category: Clippy is not doing the correct thing L-suggestion Lint: Improving, adding or fixing lint suggestions labels Dec 12, 2018
@oxalica
Copy link
Contributor

oxalica commented May 26, 2019

+1 for this issue.

I also met it when calling a function requiring Range. Example:

fn foo(_: std::ops::Range<i32>) {}

fn main() {
    let x = 1;
    foo(x..(x + 1));
}

I don't think implementing a generic function for all ranges would be always better.

@flip1995
Copy link
Member

Citing #4898 to keep traack of this in a single issue:

warning: an exclusive range would be more readable
  --> src/main.rs:55:21
   |
55 |         init_range: 0..=(sidx_offset - 1),
   |                     ^^^^^^^^^^^^^^^^^^^^^ help: use: `0..sidx_offset`
   |
   = note: `#[warn(clippy::range_minus_one)]` on by default
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#range_minus_one

However, init_range in the struct in question, is defined as an InclusiveRange. (By design, matching HTTP-Range).

So the same problem exists the other way around.

@flip1995 flip1995 changed the title range_plus_one lint wrongly suggests using RangeInclusive when Range is required range_plus_one lint wrongly suggests using RangeInclusive when Range is required and the other way around Dec 22, 2019
@kvark
Copy link

kvark commented Dec 31, 2019

We hit this in gfx-rs since the code expects Range specifically and fails upon turning the expression into RangeInclusive.

@jyn514
Copy link
Member

jyn514 commented Jan 13, 2020

This also happens when returning a value from a function.

pub struct Span {
    offset: usize,
    length: usize,
}

impl From<Span> for RangeInclusive<usize> {
    fn from(span: Span) -> Self {
        // clippy wants me to put span.offset..(span.offset + span.length) here
        span.offset..=(span.offset + span.length - 1)
    }
}

@krishna-veerareddy
Copy link
Contributor

@flip1995 Hey I would like to tackle this one but I am not sure how we would know if the type of an expression can be changed or not. Any suggestions?

chrisduerr added a commit to chrisduerr/rust-clippy that referenced this issue Jul 6, 2020
This moves the range_minus_one lint to the pedantic category, so there
will not be any warnings emitted by default. This should work around
problems where the suggestion is impossible to resolve due to the range
consumer only accepting a specific range implementation, rather than the
`RangeBounds` trait (see rust-lang#3307).

While it is possible to work around this by extracting the boundary into
a variable, I don't think clippy should encourage people to disable or
work around lints, but instead the lints should be fixable. So hopefully
this will help until a proper implementation checks what the range is
used for.
chrisduerr added a commit to chrisduerr/rust-clippy that referenced this issue Jul 10, 2020
This moves the range_minus_one lint to the pedantic category, so there
will not be any warnings emitted by default. This should work around
problems where the suggestion is impossible to resolve due to the range
consumer only accepting a specific range implementation, rather than the
`RangeBounds` trait (see rust-lang#3307).

While it is possible to work around this by extracting the boundary into
a variable, I don't think clippy should encourage people to disable or
work around lints, but instead the lints should be fixable. So hopefully
this will help until a proper implementation checks what the range is
used for.
bors added a commit that referenced this issue Jul 13, 2020
Move range_minus_one to pedantic

This moves the range_minus_one lint to the pedantic category, so there
will not be any warnings emitted by default. This should work around
problems where the suggestion is impossible to resolve due to the range
consumer only accepting a specific range implementation, rather than the
`RangeBounds` trait (see #3307).

While it is possible to work around this by extracting the boundary into
a variable, I don't think clippy should encourage people to disable or
work around lints, but instead the lints should be fixable. So hopefully
this will help until a proper implementation checks what the range is
used for.

*Please keep the line below*
changelog: move [`range_minus_one`] to pedantic
flip1995 added a commit to flip1995/rust-clippy that referenced this issue Jul 13, 2020
Move range_minus_one to pedantic

This moves the range_minus_one lint to the pedantic category, so there
will not be any warnings emitted by default. This should work around
problems where the suggestion is impossible to resolve due to the range
consumer only accepting a specific range implementation, rather than the
`RangeBounds` trait (see rust-lang#3307).

While it is possible to work around this by extracting the boundary into
a variable, I don't think clippy should encourage people to disable or
work around lints, but instead the lints should be fixable. So hopefully
this will help until a proper implementation checks what the range is
used for.

*Please keep the line below*
changelog: move [`range_minus_one`] to pedantic
flip1995 added a commit to flip1995/rust-clippy that referenced this issue Jul 13, 2020
Move range_minus_one to pedantic

This moves the range_minus_one lint to the pedantic category, so there
will not be any warnings emitted by default. This should work around
problems where the suggestion is impossible to resolve due to the range
consumer only accepting a specific range implementation, rather than the
`RangeBounds` trait (see rust-lang#3307).

While it is possible to work around this by extracting the boundary into
a variable, I don't think clippy should encourage people to disable or
work around lints, but instead the lints should be fixable. So hopefully
this will help until a proper implementation checks what the range is
used for.

*Please keep the line below*
changelog: move [`range_minus_one`] to pedantic
@ThibsG
Copy link
Contributor

ThibsG commented Jan 8, 2021

@rustbot label +L-suggestion-causes-error

@rustbot rustbot added the I-suggestion-causes-error Issue: The suggestions provided by this Lint cause an ICE/error when applied label Jan 8, 2021
@ghost
Copy link

ghost commented Sep 5, 2022

@rustbot claim

@rustbot rustbot assigned ghost Sep 5, 2022
@ghost ghost removed their assignment Jan 17, 2023
@sendittothenewts
Copy link

I've just tripped this lint in some code that uses the .end() method, so applying the suggestion would've introduced an off-by-one bug that would not have been caught by the compiler, unlike the examples mentioned above. That strikes me as pretty pernicious even for a pedantic allow-by-default lint.

Of course, some of the blame for that could probably be directed at Range and RangeInclusive reusing the same .end() name for two subtly different semantics, but that's another matter.

@flip1995
Copy link
Member

@sendittothenewts Can you post a small code snippet where you ran into this issue? Doesn't have to be the original code, just a snippet that illustrates the problem.

@sendittothenewts
Copy link

sendittothenewts commented May 22, 2024

Ah yes, since end in only a method on RangeInclusive, and a field on the other Range types, there actually would be a compile failure in the specific case I mentioned, and you'd have to apply the compiler's next suggestion to remove the method call brackets before the off-by-one bug appears.

However, the following slight variation does exhibit changed run-time behaviour without any more warnings:

fn main() {
	let names = ["fred", "barney", "wilma"];
	let bound = ..=names.len() - 1; // clippy suggests `..names.len()` instead

	println!("{}", bound.end); // Would print "3" instead of "2"
	println!("{}", names[bound.end - 1]); // Would print "wilma" instead of "barney"
	println!("{}", names[bound.end]); // Would panic instead of printing "wilma"
}

@flip1995
Copy link
Member

flip1995 commented May 22, 2024

I see, so the main problem is with RangeToInclusive (note the To). Maybe the lint should at least emit a note pointing out this potential problem. The applicability of this lint should definitely be MaybeIncorrect.

@sendittothenewts
Copy link

Yes, that's right. Without the To, the problem is still there, but at least there happens to be another compiler error to bring it to your attention.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: Clippy is not doing the correct thing I-suggestion-causes-error Issue: The suggestions provided by this Lint cause an ICE/error when applied L-suggestion Lint: Improving, adding or fixing lint suggestions
Projects
None yet
Development

No branches or pull requests

10 participants