rust-lang / cargo

The Rust package manager
https://doc.rust-lang.org/cargo
Apache License 2.0
12.66k stars 2.4k forks source link

Inherited workspace.lints.clippy ignored when RUSTFLAGS=-Dwarnings #14645

Open heaths opened 5 days ago

heaths commented 5 days ago

Problem

We have a monorepo where we want to manage some lints at the workspace level. We also have some CI scripts that set RUSTFLAGS=-Dwarnings. For crates that set lints.workspace = true, lint configuration is ignored. I've even tried setting the priority as discussed in #12208 et. al. to no avail.

If I simply set e.g., lints.clippy.useless_format = "allow" in the specific crates' Cargo.toml, no warnings-as-errors are emitted.

Steps

  1. Create a workspace Cargo.toml with the content:
    [workspace.lints.clippy]
    useless_format = "allow" # or even { level = "allow", priority = -1 }
  2. Create a crate Cargo.toml with content:
    [lints]
    workspace = true
  3. Add some code to trigger the example lint. In our case, it's generated code where almost all strings will have format specifiers and determining otherwise for a few instances didn't seem worth the cost.
    fn example() -> String {
      format!("/some/path")
    }
  4. Run the following command. Notice that clippy::useless_format warnings-as-errors are still emitted and the command fails.
    # Also tried on 1.74.0, 1.76.0, and nightly to same effect.
    # Stable for the repo is 1.74, but system-wide is 1.81 and still repros.
    RUSTFLAGS=-Dwarnings cargo +stable clippy --workspace
  5. Change step 2 content to:
    [lints.clippy]
    useless_format = "allow"
  6. Rust step 4 command again. Notice no errors are emitted.

Possible Solution(s)

No response

Notes

No response

Version

No response

ehuss commented 5 days ago

Hm, unfortunately I'm not able to reproduce that. Can you include a .zip file of a workspace the demonstrates the issue?

heaths commented 5 days ago

FWIW, I opened it here because my assumption was that cargo was not "flatting" the package manifest (if it even does that) before lint tables were parsed. Certainly possible this is an issue in clippy itself. In the course of trying to troubleshoot the issue, it seems plenty of people were successfully using workspace.lints.rust.

heaths commented 5 days ago

Hm, unfortunately I'm not able to reproduce that. Can you include a .zip file of a workspace the demonstrates the issue?

@ehuss certainly, but it'll take me a bit. Our main branch as it exists right (commit b16e8790a1d0ebe608584db675b4042796b35caf) now at https://github.com/Azure/azure-sdk-for-rust exhibits all this if you were to run RUSTFLAGS=-Dwarnings cargo +stable clippy --package azure_storage_blobs but I'm happy to try to provide a minimal repro.

To note, I went ahead and fixed the bug that was unnecessarily using format!() in some cases so main won't have this problem next week sometime. Initially it didn't seem worth the time fixing it, but I ended up spending more time troubleshooting this issue. 🤦‍♂️

weihanglo commented 5 days ago

I cannot reproduce it either. Guess there were something cached between the first and the second invocation. You might want to run cargo clean in between.

The implementation of [lints] table is as simple as passing corresponding -A clippy::useless_format rustc flag to rustc invocation. You could run cargo clippy with --verbose and check if -Dwarnings is passed to rustc after -A clippy::useless_format. If I am not wrong, -Dwarnings should be the last flag.

heaths commented 5 days ago

It took me a bit - I couldn't repro it either at first - but then I figured out what my minimal repro was missing: an empty [workspace.lints] section in the root Cargo.toml. That was there to satisfy cargo since we've been injecting lints.workspace = true in all our newer crates.

See https://github.com/heaths/rust-lang-cargo-14645, specifically https://github.com/heaths/rust-lang-cargo-14645/commit/19378e57508a3c256e51703c3be4216c34eed288. Note that the root crate imports a dependency by path, but that dependency is part of a separate workspace.

In https://github.com/azure/azure-sdk-for-rust we are generating dozens of crates that we put into a separate workspace to avoid rust-analyzer perf degredation with hand-coded crates. One of those hand-coded crates is using the generated crate in its underlying implementation. That hand-coded crate has lints.workspace = true in a workspace with an empty [workspace.lints]. The generated crate also has lints.workspace = true but its workspace had a non-empty [workspace.lints.clippy] as documented above.

So somehow that empty workspace.lints section is affecting the path-imported dependency. If I put the useless_format = "allow" in the root Cargo.toml and remove it from the deps/Cargo.toml workspace, it still works. Seems the empty [workspace.lints] is overwriting the [workspace.lints.clippy] of the separate workspace. See https://github.com/heaths/rust-lang-cargo-14645/pull/1 for an example of how the separate root workspace is impacting the crate from another workspace. Is that expected?

epage commented 2 days ago

https://github.com/heaths/rust-lang-cargo-14645,

What am I supposed to do with this to reproduce the issue?

heaths commented 2 days ago

Clone the repo and run cargo clippy from the root. You'll get a clippy::useless_format for deps/foo warning despite that crate's workspace (deps/Cargo.toml) allowing it. If you add the allow to the root workspace's Cargo.toml the issue goes away. So it seems the empty root workspace - of which the dependent root crate belongs - is effectively clearing out dependencies' workspace-configured lints. Conversely, "parent" workspaces like this can override "chlid" workspaces' lint configurations. Seems those two should remain separate. Granted, the dependency is also included by path (monorepo example), or should that be expected?

epage commented 2 days ago

This also reproduces with rustc which is a more minimal case as an implementation detail of Cargo's development. I switched this out for unused_variables and added a let unused = ""; in the function with the useless_format

I put in some debug statements in Cargo

[src/cargo/util/toml/mod.rs:318:9] &package_name = PackageName(
    "rust-lang-cargo-14645",
)
[src/cargo/util/toml/mod.rs:831:5] normalized_path = "/home/epage/src/personal/dump/rust-lang-cargo-14645/Cargo.toml"
[src/cargo/util/toml/mod.rs:504:9] &normalized_lints = Some(
    {},
)
[src/cargo/util/toml/mod.rs:318:9] &package_name = PackageName(
    "foo",
)
[src/cargo/util/toml/mod.rs:831:5] normalized_path = "/home/epage/src/personal/dump/rust-lang-cargo-14645/deps/foo/Car
go.toml"
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645/deps/foo"
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645/deps"
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645"
[src/cargo/util/toml/mod.rs:859:5] &workspace_path = "/home/epage/src/personal/dump/rust-lang-cargo-14645/Cargo.toml"
[src/cargo/util/toml/mod.rs:504:9] &normalized_lints = Some(
    {},
)
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645/deps/foo"
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645/deps"
[src/cargo/core/workspace.rs:1991:13] current = "/home/epage/src/personal/dump/rust-lang-cargo-14645"

The bug appears to be that our caching of parsed workspaces isn't taking into account all of the workspace load rules. This is a problem for all of workspace inheritance. I'm assuming this hasn't been found before as workspaces aren't usually nested but parallel to each other.