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

Extend overlapping ranges lint to cover cases with more than a single element overlapping #65477

Open
estebank opened this issue Oct 16, 2019 · 12 comments
Labels
A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. I-needs-decision Issue: In need of a decision. T-lang Relevant to the language team, which will review and decide on the PR/issue.

Comments

@estebank
Copy link
Contributor

estebank commented Oct 16, 2019

#64007 introduces an overlapping ranges lint that triggers only if the beginning or the end overlap with another arms' pattern, like in 0..10/9..20. There should be a conversation on whether it should also trigger when the overlap is beyond that, like in 0..10/5..15.

@estebank estebank added A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. I-nominated I-needs-decision Issue: In need of a decision. T-lang Relevant to the language team, which will review and decide on the PR/issue. labels Oct 16, 2019
@nikomatsakis
Copy link
Contributor

We discussed this in today's @rust-lang/lang meeting. I wouldn't say that there was much of a strong opinion. On the one hand, you can imagine cases where you would intentionally have overlap, but that's always true, and the scenario didn't seem super compelling (example: pre-existing constants for various ranges, and you want to give one range precedence). On the other hand, this doesn't feel like something that arises super often.

We wanted to propose a few options:

  • do a crater run to get an idea of how often this comes up in practice and how many of those cases feel like bugs -- this might help to decide for a warn-by-default lint
  • create an allow-by-default lint for this scenario (and perhaps others, like full subsumption)
  • move the lint to clippy

The last option might be impractical, because the analysis is fairly involved in the compiler. Hence the middle option.

@calimeroteknik
Copy link

calimeroteknik commented Oct 13, 2020

If I may chime in, as a new user it was utterly confusing when I found out that #![deny(overlapping_patterns)] did not always deny overlapping patterns. This inspired me a quite a bit of distrust in the rust compiler.

Unless the ranges 0..10 and 5..15 can be defined as "not overlapping" (!?), but that seems blatantly false and unarguable to me.

I would conclude that either :

  • overlapping_patterns should become what it sounds like, meaning #![deny(overlapping_patterns)] should deny all overlapping patterns, not just some of them;
  • and/or, the current behaviour should be renamed to something else, like pattern_boundary_clash.

This is of course a breaking change, so the choice is whether to keep the confusing lint name for backwards compatibility, or to bite the bullet and change it to something that is actually what it looks like.

Edit: I should mention that clippy does apparently catch all overlapping range patterns, from what I tried. toy example

@Nadrieril
Copy link
Member

Nadrieril commented Oct 21, 2020

Note: clippy only detects the simple case where there is only a bare range pattern. A more complex pattern doesn't get detected. This is also what the compiler does for this lint. To go beyond that simple case would indeed require the full power of the compiler's exhaustiveness checker.

match x { // single range pattern
    0 ..= 125 => {}
    125 ..= 255 => {} // overlap detected
}
match x { // anything else
    (0 ..= 125, true) => {}
    (125 ..= 255, true) => {} // overlap not detected
}

@Nadrieril
Copy link
Member

So, I've been trying to extend the existing lint beyond that simple case. Turns out this does not fit naturally with what the exhaustiveness algorithm currently does. I'm also not sure exactly what we would want to lint:

// Presumably we want to lint about overlap between the third and first
// branches, but not the second. This would require keeping track of which
// branches made a pattern redundant.
match x {
    (0 ..= 125, true, true) => {}
    (0 ..= 125, true, false) => {}
    (125 ..= 255, true, true) => {}
}
// If we also want a similar lint for this one, then this requires computing
// pattern intersections, which is something that the algorithm does not know
// how to do at all.
match x {
    (0 ..= 125, true, true) => {}
    (0 ..= 125, false, false) => {}
    (125 ..= 255, true, _) => {}
}

It is doable, but looks like a massive undertaking. I'd be happy to implement that if I felt this was worth it, but it looks too small for that kind of complexity penalty.
The alternative is living with a half-baked lint that only applies to the simplest patterns.

@calimeroteknik
Copy link

By the way, it is obvious that when a default case is present (and isn't the only case present), overlap is by design: the default case overlaps all other patterns.

In that case, I wouldn't expect the compiler to even try to detect overlap between patterns.

Idea: in the cases where the compiler cannot check the absence of overlap between patterns (because they're too complex), it could warn that it was unable to do so. Also, in the presence of a default case, such a warning could be silenced by default.

@varkor
Copy link
Member

varkor commented Oct 22, 2020

I think we should simply rename the existing lint (there's precedent for doing this, and we can warn users a lint name has changed) to make it less confusing.

@Nadrieril
Copy link
Member

I think we should simply rename the existing lint (there's precedent for doing this, and we can warn users a lint name has changed) to make it less confusing.

Agreed; I'd go with suspiciously_overlapping_ranges.

@varkor
Copy link
Member

varkor commented Oct 22, 2020

overlapping_endpoints is descriptive and hopefully unambiguous, or overlapping_range_endpoints.

@Nadrieril
Copy link
Member

I can make a PR to change the lint name. I also believe I only need to add a line here to get a rename warning:

// Register renamed and removed lints.
store.register_renamed("single_use_lifetime", "single_use_lifetimes");

Is that the right place?

@rustbot claim
@rustbot modify labels: +A-exhaustiveness-checking

@rustbot rustbot added the A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns label Oct 22, 2020
@varkor
Copy link
Member

varkor commented Oct 22, 2020

Yes, that looks right.

@Nadrieril Nadrieril removed their assignment Oct 23, 2020
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Dec 21, 2020
…nts-lint, r=varkor

Rename `overlapping_patterns` lint

As discussed in rust-lang#65477. I also tweaked a few things along the way.

r? `@varkor`
`@rustbot` modify labels: +A-exhaustiveness-checking
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Dec 22, 2020
…nts-lint, r=varkor

Rename `overlapping_patterns` lint

As discussed in rust-lang#65477. I also tweaked a few things along the way.

r? ``@varkor``
``@rustbot`` modify labels: +A-exhaustiveness-checking
Dylan-DPC-zz pushed a commit to Dylan-DPC-zz/rust that referenced this issue Dec 22, 2020
…nts-lint, r=varkor

Rename `overlapping_patterns` lint

As discussed in rust-lang#65477. I also tweaked a few things along the way.

r? ```@varkor```
```@rustbot``` modify labels: +A-exhaustiveness-checking
bors added a commit to rust-lang-ci/rust that referenced this issue Dec 22, 2020
…s-lint, r=varkor

Rename `overlapping_patterns` lint

As discussed in rust-lang#65477. I also tweaked a few things along the way.

r? `@varkor`
`@rustbot` modify labels: +A-exhaustiveness-checking
@Nadrieril
Copy link
Member

Coming back to this, I think this could be a more general clippy lint, something like "this pattern could be more specific". It would say "use 11..20 instead of 10..20 here", or "use Some(_) instead of _ here". This feels doable once we've librarified exhaustiveness checking (which I'm slowly working towards).

@Nadrieril
Copy link
Member

Exhaustiveness checking has now been librarified and is used in rust-analyzer. I someone wants to make this into a clippy lint, I can assist with how to use rustc_pattern_analysis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-exhaustiveness-checking Relating to exhaustiveness / usefulness checking of patterns A-lint Area: Lints (warnings about flaws in source code) such as unused_mut. I-needs-decision Issue: In need of a decision. T-lang Relevant to the language team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

7 participants