diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 00000000000..acf3d2d097a --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,45 @@ +name: clippy +on: + merge_group: + pull_request: + +permissions: + contents: read + +jobs: + check_clippy_configs: + runs-on: ubuntu-latest + name: check clippy configs + steps: + - name: checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + rustup target add x86_64-unknown-linux-gnu + - name: Check clippy configs all match + run: cargo run --manifest-path ci/Cargo.toml -- clippy-config-check + + + run_clippy: + runs-on: ubuntu-latest + name: clippy ${{ matrix.package }} + strategy: + fail-fast: false + matrix: + package: + - . # rustfmt + - check_diff + - ci + - config_proc_macro + steps: + - name: checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: install rustup + run: | + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs > rustup-init.sh + sh rustup-init.sh -y --default-toolchain none + - name: clippy + run: cargo clippy --manifest-path ${{ matrix.package }}/Cargo.toml + diff --git a/Cargo.toml b/Cargo.toml index 48dfeed93da..7e4d2e60565 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -69,3 +69,17 @@ insta = { version = "1.48.0", features = ["filters"] } [package.metadata.rust-analyzer] # This package uses #[feature(rustc_private)] rustc_private = true + +[lints.clippy] +# allow all rules with a low priority .. +all = { level = "allow", priority = -1 } +# so we can pick out an exact subset of rules to deny +correctness = "deny" +char_lit_as_u8 = "deny" +four_forward_slashes = "deny" +non_minimal_cfg = "deny" +print_literal = "deny" +same_item_push = "deny" +single_char_add_str = "deny" +to_string_in_format_args = "deny" +unconditional_recursion = "deny" diff --git a/Contributing.md b/Contributing.md index 99558079984..ebf1d874f30 100644 --- a/Contributing.md +++ b/Contributing.md @@ -17,6 +17,26 @@ assistance when participating in `rust-lang/rustfmt`. [LLM usage policy]: https://forge.rust-lang.org/policies/llm-usage.html +## Linting + +This project supports linting via +[`clippy`](https://doc.rust-lang.org/stable/clippy/index.html). You can either +run it directly, or use an integration through your editor. + +``` +# lint rustfmt-nightly (the package at the root of this repo) +cargo clippy +``` + +To Lint a package outside of the root one you will need to do one of: + +``` +# pass the manifest path +cargo clippy --manifest-path ./config_proc_macro/Config.toml +# or run the command from within the package +cd ./config_proc_macro +cargo clippy +``` ## Test and file issues diff --git a/ci/Cargo.lock b/ci/Cargo.lock index 7c7807f6f37..caa4dfaad41 100644 --- a/ci/Cargo.lock +++ b/ci/Cargo.lock @@ -5,3 +5,153 @@ version = 4 [[package]] name = "ci-integration" version = "0.0.1" +dependencies = [ + "itertools", + "toml", +] + +[[package]] +name = "either" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "indexmap" +version = "2.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b4baf93f58d4425749ca49a51c50ebab072c5df6994d08fed93541c331481dc" +dependencies = [ + "either", +] + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "syn" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "toml" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12c0ba9680044b4ce98d391a62094047eada0d64860b80166c39f4a6b5640785" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" diff --git a/ci/Cargo.toml b/ci/Cargo.toml index 74312216d4e..a03a11aa8aa 100644 --- a/ci/Cargo.toml +++ b/ci/Cargo.toml @@ -5,3 +5,21 @@ edition = "2024" publish = false [workspace] + +[dependencies] +itertools = "0.15.0" +toml = "1.1.5" + +[lints.clippy] +# allow all rules with a low priority .. +all = { level = "allow", priority = -1 } +# so we can pick out an exact subset of rules to deny +correctness = "deny" +char_lit_as_u8 = "deny" +four_forward_slashes = "deny" +non_minimal_cfg = "deny" +print_literal = "deny" +same_item_push = "deny" +single_char_add_str = "deny" +to_string_in_format_args = "deny" +unconditional_recursion = "deny" diff --git a/ci/src/clippy_config_check.rs b/ci/src/clippy_config_check.rs new file mode 100644 index 00000000000..011ffe4ef18 --- /dev/null +++ b/ci/src/clippy_config_check.rs @@ -0,0 +1,40 @@ +use std::fs; + +use itertools::Itertools; + +const CARGO_TOMLS: &[&'static str] = &[ + "Cargo.toml", + "ci/Cargo.toml", + "config_proc_macro/Cargo.toml", +]; + +pub(crate) fn runner() -> Result<(), String> { + for (first, second) in CARGO_TOMLS.iter().tuple_windows() { + let first_table = parse_to_toml(&first)?; + let second_table = parse_to_toml(&second)?; + assert_eq!( + get_lints(first, &first_table)?, + get_lints(second, &second_table)?, + "Clippy configs do not match between {} and {}", + first, + second, + ); + } + Ok(()) +} + +fn parse_to_toml(path: &str) -> Result { + let toml_str = fs::read_to_string(path).map_err(|e| format!("reading {}: {}", path, e))?; + toml_str + .parse::() + .map_err(|e| format!("parsing {} as TOML: {}", path, e)) +} + +fn get_lints<'a>(path: &str, toml: &'a toml::Table) -> Result<&'a toml::Value, String> { + toml.get("lints") + .ok_or(format!("{} is missing key {}", path, "lints")) + .and_then(|t| { + t.get("clippy") + .ok_or(format!("{} is missing key {}", path, "lints.clippy")) + }) +} diff --git a/ci/src/main.rs b/ci/src/main.rs index 4817fa3c49c..e7c4a2b09dd 100644 --- a/ci/src/main.rs +++ b/ci/src/main.rs @@ -1,4 +1,5 @@ mod build_and_test; +mod clippy_config_check; mod common; mod integration; @@ -7,11 +8,14 @@ fn main() { if let Err(error) = match args.next().as_deref() { Some("integration") => integration::runner(&mut args), Some("build-and-test") => build_and_test::runner(), + Some("clippy-config-check") => clippy_config_check::runner(), Some(arg) => Err(format!( - "Expected `integration` or `build-and-test` as first argument, found {arg:?}" + "Expected `integration`, `build-and-test`, or `clippy-config-check` \ + as first argument, found {arg:?}" )), None => Err( - "Expected `integration` or `build-and-test` as first argument, found nothing" + "Expected `integration`, `build-and-test`, or `clippy-config-check` \ + as first argument, found nothing" .to_string(), ), } { diff --git a/config_proc_macro/Cargo.toml b/config_proc_macro/Cargo.toml index ec0db49d71c..48d894577ef 100644 --- a/config_proc_macro/Cargo.toml +++ b/config_proc_macro/Cargo.toml @@ -21,3 +21,17 @@ serde = { version = "1.0.160", features = ["derive"] } [features] default = [] debug-with-rustfmt = [] + +[lints.clippy] +# allow all rules with a low priority .. +all = { level = "allow", priority = -1 } +# so we can pick out an exact subset of rules to deny +correctness = "deny" +char_lit_as_u8 = "deny" +four_forward_slashes = "deny" +non_minimal_cfg = "deny" +print_literal = "deny" +same_item_push = "deny" +single_char_add_str = "deny" +to_string_in_format_args = "deny" +unconditional_recursion = "deny"