Skip to content

ptr_arg doesn't work with impl AsRef<Path> #15506

@ada4a

Description

@ada4a

Summary

When checking for the usages of a function argument of type &PathBuf, the lint incorrectly considers it unlintable if it's used as an argument to a function whose parameter at that place is impl AsRef<Path> (as is most often the case with functions that work on paths). The lint doesn't realize that &Path would work there just as well.

I noticed this when looking into #15504, but decided not to start working on it for now, because of the feature freeze. But I've made a couple of test cases:

mod issue15505 {
    use std::path::{Path, PathBuf};

    fn takes_asref_path(_: &dyn AsRef<Path>) {}
    fn takes_independent_asref_paths(_: impl AsRef<Path>, _: impl AsRef<Path>) {}
    fn takes_same_asref_paths<P: AsRef<Path>>(_: P, _: P) {}

    fn simple(p: &PathBuf) {
        //~^ ptr_arg
        takes_asref_path(p);
    }

    fn can_replace_when_independent_path(p: &PathBuf) {
        //~^ ptr_arg
        takes_independent_asref_paths(p, Path::new("foo"))
    }

    fn can_replace_when_independent_pathbuf(p: &PathBuf) {
        //~^ ptr_arg
        takes_independent_asref_paths(p, &PathBuf::new())
    }

    fn cant_replace_when_dependent(p: &PathBuf) {
        // because of the second arg, the first one needs to be `&PathBuf` as well
        takes_same_asref_paths(p, &PathBuf::new())
    }
}

If I end up forgetting about this, feel free to pick it up.

Some hints:
The lint already works with parameters like dyn AsRef<Path>, but that's because for those, the argument is just a bunch of trait bounds (which we check against for the deref type), whereas with impl AsRef<Path> we see only the function instantiated for our particular type. We should probably:

  1. resolve its path
  2. see if the argument our thing is passed in is a generic P
  3. make sure that P isn't used in any other parameters (see the last test)
  4. check whether our deref type implements the trait bounds of P as well (using the logic similar to that for dyn)

Lint Name

ptr_arg

Reproducer

I tried this code:

fn takes_asref_path(_: impl AsRef<Path>) {}

fn simple(p: &PathBuf) {
    takes_asref_path(p);
}

I expected to see this happen:

error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
  --> tests/ui/ptr_arg.rs:451:18
   |
LL |     fn simple(p: &PathBuf) {
   |                  ^^^^^^^^ help: change this to: `&Path`

Instead, this happened:

Version

rustc 1.91.0-nightly (7d82b83ed 2025-08-06)
binary: rustc
commit-hash: 7d82b83ed57d188ab3f2441a765a6419685a88a3
commit-date: 2025-08-06
host: x86_64-unknown-linux-gnu
release: 1.91.0-nightly
LLVM version: 21.1.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-bugCategory: Clippy is not doing the correct thingI-false-negativeIssue: The lint should have been triggered on code, but wasn't

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions