Skip to content

clippy beta 1.87 lints all pointer comparisons, possibly too much? #14525

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

Closed
davidhewitt opened this issue Apr 2, 2025 · 14 comments · Fixed by #14526
Closed

clippy beta 1.87 lints all pointer comparisons, possibly too much? #14525

davidhewitt opened this issue Apr 2, 2025 · 14 comments · Fixed by #14526
Assignees
Labels
C-bug Category: Clippy is not doing the correct thing

Comments

@davidhewitt
Copy link

davidhewitt commented Apr 2, 2025

Summary

As of clippy beta 1.87 the ptr_eq lint seems to be forbidding all pointer equality.

I think this seems like a regression, as far as I can tell the ptr_eq lint is intended to help in cases where &T references are being cast to pointers and then compared. Using std::ptr::eq can coerce those references and conveys intention cleanly.

On the other hand, if you already have pointers, calling std::ptr::eq doesn't seem necessary to me?

Reproducer

I tried this code:

pub fn foo() -> bool {
    let a: *const i32 = std::ptr::null();
    let b: *const i32 = std::ptr::null();
    
    a == b
}

I expected to see this happen:

Lint ok

Instead, this happened:

Clippy suggests replacing a == b with a call to std::ptr::eq.

Version

0.1.87 (2025-04-01 45165c82a4)

Additional Labels

No response

@davidhewitt davidhewitt added the C-bug Category: Clippy is not doing the correct thing label Apr 2, 2025
@davidhewitt
Copy link
Author

cc @samueltardieu @Manishearth

My guess is that #14339 introduced this change, which is titled "lint more cases" but the ticket it closed seems to be a bug rather than a proposal to make this lint different. The PR also didn't update the motivation for "why is this bad" at all.

In particular this expansion of the lint is going to hit code using raw pointers quite hard, e.g. we noticed this in pyo3-ffi.

Maybe this change of behaviour to more aggressively forbid == on pointers really is intended. The motivation for it and the churn it may cause when this hits stable doesn't seem justified from what I can see in those discussions.

For what it's worth, I'd argue against this change of behaviour. In my opinion a == b is cleaner than special casing eq(a, b) if a and b are already pointers. Why add the cognitive overhead of a special case?

(On the other hand, I can completely support the lint as it currently functions on stable to make comparison of references by pointer much clearer.)

@Manishearth
Copy link
Member

I think the change was intentional: there's a couple footguns in this area, and potentially more with provenance, so encouraging this style overall seems good.

That said, perhaps we can make it not lint for simple cases where there is no casting and the types are both raw? @samueltardieu , what do you think

cc @rust-lang/clippy on whether we should back this out, do a fix, or leave as is

@mejrs
Copy link

mejrs commented Apr 2, 2025

See https://github.com/PyO3/pyo3/pull/5032/files for what the difference looks like. Personally I find it a bit too churn-y.

@samueltardieu samueltardieu self-assigned this Apr 2, 2025
@samueltardieu
Copy link
Contributor

I think the change was intentional: there's a couple footguns in this area, and potentially more with provenance, so encouraging this style overall seems good.

That said, perhaps we can make it not lint for simple cases where there is no casting and the types are both raw? @samueltardieu , what do you think

cc @rust-lang/clippy on whether we should back this out, do a fix, or leave as is

I'll do some experiments to see how less linting would look like.

@Manishearth
Copy link
Member

See https://github.com/PyO3/pyo3/pull/5032/files for what the difference looks like. Personally I find it a bit too churn-y.

Generally we're careful about churning, but churn in and of itself isn't bad, especially with cargo clippy --fix. The question is more about whether this change is positive when compared to the amount of churn. I think it comes out mostly even from my view: the clarity in unsafe code is basically always good. But still, perhaps it doesn't actually help much here.

@samueltardieu
Copy link
Contributor

I don't find the churning bad either, but I also understand the rationale of wanting to compare two raw pointers with ==. I have proposed a change for discussion in #14526. As stated by @Manishearth it would be great to have other people's input as well.

@Alexendoo
Copy link
Member

I agree with only linting if we can remove a cast

@flip1995
Copy link
Member

flip1995 commented Apr 3, 2025

If we should change something with this lint, please label the PR with beta-nominates, so that the current state doesn't hit stable.

@davidhewitt
Copy link
Author

To add a counter-argument, in the PyO3 thread it was pointed out to me that using ptr::eq does have the upside of making it very clear that the operation is using pointer equality, not value equality.

I could accept that motivation for preferring ptr::eq. if that is decided as preferred I think it would be worth updating the lint documentation to suggest that.

@Darksonn
Copy link

I've run into this lint change as well. I think there's a pretty big difference between:

  • I have a &T and Arc<T> and I need to see if the &T comes from the Arc.
  • I have a linked list with a lot of raw pointers that I need to compare.

In the first case, I want ptr::eq for clarity, in the second case it is not helpful.

If we're not going to revert this, can we please split this lint into two so that I can configure it to trigger on the first case but not the second? I believe the previously suggested distinction of whether you get rid of a cast is a good way to distinguish them.

@ojeda
Copy link
Contributor

ojeda commented Apr 22, 2025

Yeah, this is another case of "major addition to an existing lint" -- in general, splitting new functionality into new lints (or at least making it opt-in via the Clippy config) would make it much easier for big projects to keep their builds Clippy-clean, especially for those that have stable branches that need backports like the Linux kernel, without resorting to globally allowing the lint (or deciding not to keep the stable branches' builds Clippy-clean).

A similar example happened recently in needless_continue in the previous release 1.86 (#14536).

Perhaps something to talk about in RustWeek?

github-merge-queue bot pushed a commit that referenced this issue Apr 22, 2025
`ptr_eq` was recently enhanced to lint on more cases of raw pointers
comparison:

- lint on all raw pointer comparison, by proposing to use
`[core|std]::ptr::eq(lhs, rhs)` instead of `lhs == rhs`;
- removing one symetric `as usize` on each size if needed
- peeling any level of `as *[const|mut] _` if the remaining expression
can still be coerced into the original one (i.e., is a ref or raw
pointer to the same type as before)

The current change restricts the lint to the cases where at least one
level of symetric `as usize`, or any conversion to a raw pointer, could
be removed. For example, a direct comparaison of two raw pointers will
not trigger the lint anymore.

changelog: [`ptr_eq`]: do not lint when comparing two raw pointers
directly with no casts involved

Fixes #14525
@Manishearth
Copy link
Member

@ojeda It's tricky: For every one such case that hits a lot of users, there are many that do not and represent a reasonable incremental change to a lint. It's not always easy to predict this beforehand. Not doing this at all would cut down on a ton of clippy changes that people actually want. There's a delicate balance to be had here.

When we notice this happening we do try and split the lint, or fix it to not be as noisy: we went for the latter here.

So this is a known issue, and the way it was handled here is roughly the system working as it should, though it should not have taken that long to land a fix, ideally. If we had known in advance that it would take that time, we probably would have temporarily made it a config.

@ojeda
Copy link
Contributor

ojeda commented Apr 23, 2025

Changes are welcome, of course, i.e. it is great that Clippy keeps adding useful lints and improving existing ones. It is just about how to add "major" new cases.

Given what you say, the key seems to be detecting whether a change hits a lot of users or not, and this typically happens when the changes land in nightly and end users test them, if I understand correctly.

I imagine having some early pre-merge way of triggering a very small crater-like run (or something like that) was considered or maybe it is already possible -- if it could be useful to add the Linux kernel there as a meaningful data point, please let us know.

On the other hand, I guess sometimes it is just better to wait until end users tell you what they think about a change, and thus waiting for nightly is simpler -- in this case, it is good to know that Clippy changes can be easily reconsidered during nightly (and beta, hopefully), i.e. sometimes it can be hard to tell from the end user point of view whether a change in a project is more on the "fairly set in stone" side or the "undergoing testing/consideration" one, if that makes sense.

Thanks!

@Manishearth
Copy link
Member

Yeah, we have lintcheck for that, but it's a delicate balance and highly subjective. There are cases when we predict it correctly, and cases when we don't. This will continue to happen, we've done a fair amount of work to ensure it doesn't happen all the time, but it's not really possible to avoid it completely.

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
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants