Skip to content

New lint: disallowed_trait_usage#16810

Open
emilk wants to merge 6 commits intorust-lang:masterfrom
emilk:emilk/lint/disallowed_trait_usage
Open

New lint: disallowed_trait_usage#16810
emilk wants to merge 6 commits intorust-lang:masterfrom
emilk:emilk/lint/disallowed_trait_usage

Conversation

@emilk
Copy link
Copy Markdown

@emilk emilk commented Apr 4, 2026

New lint: disallowed_trait_usage


This adds a new configurable lint [disallowed_trait_usage] that lets users forbid using a type via a specific trait interface.

Motivation

A common mistake is using Debug formatting ({:?}) where Display formatting ({}) would be more appropriate, especially for user-facing output like error messages, logs, and GUIs. For example:

// Bad: Debug formatting of errors is ugly and implementation-specific
let err: std::io::Error = std::fs::read("missing.txt").unwrap_err();
log::warn!("Failed to read file: {err:?}");
// prints: Failed to read file: Os { code: 2, kind: NotFound, message: "No such file or directory" }

// Good: Display formatting is human-readable
log::warn!("Failed to read file: {err}");
// prints: Failed to read file: No such file or directory (os error 2)

This applies broadly — paths, errors, and many other types have Debug output that is noisy and not meant for end users. There's no way to systematically catch these with existing lints. The unnecessary_debug_formatting lint only covers OsStr and Path, and can't be extended to arbitrary types or project-specific conventions.

Configuration

Each entry requires a trait field (the trait being disallowed) and either:

  • type: a concrete type path, or
  • implements: a trait path — matches any type implementing that trait
# clippy.toml
disallowed-trait-usage = [
    # Forbid Debug formatting of specific types:
    { type = "std::path::PathBuf", trait = "std::fmt::Debug", reason = "Use path.display() instead" },
    { type = "std::path::Path", trait = "std::fmt::Debug", reason = "Use path.display() instead" },

    # Forbid Debug formatting of ALL types implementing std::error::Error:
    { implements = "std::error::Error", trait = "std::fmt::Debug", reason = "Use Display for errors" },

    # Works with primitive types too:
    { type = "i32", trait = "std::fmt::Debug", reason = "Use Display formatting instead" },

    # Works with custom types and traits:
    { type = "my_crate::MyStruct", trait = "my_crate::MyTrait", reason = "Use a different approach" },
]

What it detects

  • Format macro arguments: println!("{:?}", my_path), format!("{path:?}"), write!(buf, "{:?}", x)
  • Direct trait method calls: my_struct.trait_method()

Works with:

  • Standard library types and traits (e.g. std::path::PathBuf + std::fmt::Debug)
  • Primitive types (i32, bool, str, etc.)
  • Custom user-defined types and traits
  • All format traits (Debug, Display, Binary, Octal, LowerHex, UpperHex, LowerExp, UpperExp, Pointer)
  • References (automatically peeled)
  • Trait-bound matching via implements (any type implementing the given trait)

Path validation

Invalid paths produce clear warnings, following the same pattern as disallowed_types/disallowed_methods:

warning: expected a trait, found a struct: `std::string::String` (in `disallowed-trait-usage`)
warning: `std::nonexistent::Foo` does not refer to a reachable type (in `disallowed-trait-usage`)
warning: `type` and `implements` are mutually exclusive (in `disallowed-trait-usage`)

changelog: [disallowed_trait_usage]: new lint that forbids using a configured type (or implementors of a trait) via a configured trait interface


Implemented with a lot of help from Claude Code

@rustbot rustbot added the needs-fcp PRs that add, remove, or rename lints and need an FCP label Apr 4, 2026
@emilk emilk marked this pull request as ready for review April 4, 2026 14:46
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Apr 4, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Apr 4, 2026

r? @dswij

rustbot has assigned @dswij.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 7 candidates
  • 7 candidates expanded to 7 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

@dswij
Copy link
Copy Markdown
Member

dswij commented Apr 4, 2026

r? clippy

@rustbot rustbot assigned llogiq and unassigned dswij Apr 4, 2026
@kayabaNerve
Copy link
Copy Markdown

I'm unclear on the value of this lint, though I don't mind people being able to choose to restrict how they write their code. The reason I comment at all is because this is approximate to an extension of an already-existing lint, disallowed_methods, which already had a PR to allow it to apply to trait methods: #12194

I'd like to ask if the trait granularity and a new lint is a better approach than reviving the existing PR to extend the existing lint (which is distinct as it would offer method granularity).

@emilk
Copy link
Copy Markdown
Author

emilk commented Apr 10, 2026

My main motivation is to be able to outlaw uses of Debug formatting for types in my codebase that implements Display. I've made several PRs where I've manually looked for Debug formatting of certain types and replaced them with Display formatting. I'd like to enforce this using clippy. Examples:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New lint: disallowed-trait-usage

5 participants