Skip to content

unconditional_recursion lint is confused by (at least) blanket PartialEq impls #12181

Closed
@HadrienG2

Description

@HadrienG2

Summary

So, I am writing an hwloc binding and in various circumstances hwloc hands me over C allocations that I am supposed to free using their routine of choice.

Our concern today is a C allocation that contains an XML string, which I model using an RAII type that has an explicit conversion to Rust string, and can be compared with either itself or any other type that can be compared with a Rust string using a pair of PartialEq impls based on the aforementioned explicit conversion.

The second PartialEq impl (which, as it turns out, did not quite do what I want and is going to be fixed) is wrongly flagged as unconditionally recursive by the unconditional_recursion lint, when actually it is fine.

I suspect that happens because the unconditional_recursion lint somehow does not understand that I'm calling the PartialEq impl of &str, not recursing into the PartialEq impl of XML<'_>. If so, the bug may not lie in PartialEq-specific logic, but in more general trait bound processing. Another thing that fuels my suspicion that this isn't a problem with PartialEq specific code is that the lint is not triggered if I replace the explicit .eq() call with an equivalent == operator expression.

Lint Name

unconditional_recursion

Reproducer

This simplified reproducer...

struct Foo;

impl Foo {
    fn as_str(&self) -> &str {
        "Foo"
    }
}

impl PartialEq for Foo {
    fn eq(&self, other: &Self) -> bool {
        self.as_str().eq(other.as_str())
    }
}

impl<T> PartialEq<T> for Foo
where
    for<'a> &'a str: PartialEq<T>
{
    fn eq(&self, other: &T) -> bool {
        (&self.as_str()).eq(other)
    }
}

fn main() {
    println!("{}", Foo == "Hello");
}

...triggers the unconditional_recursion lint:

warning: function cannot return without recursing
  --> src/main.rs:10:5
   |
10 | /     fn eq(&self, other: &Self) -> bool {
11 | |         self.as_str().eq(other.as_str())
12 | |     }
   | |_____^
   |
note: recursive call site
  --> src/main.rs:11:9
   |
11 |         self.as_str().eq(other.as_str())
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unconditional_recursion
   = note: `#[warn(clippy::unconditional_recursion)]` on by default

Because a different PartialEq implementation is called, the program is actually not unconditionally recursive, as you can check by running it. So I would expect clippy not to trigger this lint, which is incorrect in this particular situation.

Version

rustc 1.77.0-nightly (88189a71e 2024-01-19)
binary: rustc
commit-hash: 88189a71e4e4376eea82ac61db6a539612eb200a
commit-date: 2024-01-19
host: x86_64-unknown-linux-gnu
release: 1.77.0-nightly
LLVM version: 17.0.6

Additional Labels

@rustbot label +C-bug +I-false-positive +L-suspicious

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-positiveIssue: The lint was triggered on code it shouldn't haveL-suspiciousLint: Belongs in the suspicious lint group

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions