From 18b85ecf3ba8769962de65d6f234c35750c165fc Mon Sep 17 00:00:00 2001 From: Randolf Jung Date: Thu, 24 Sep 2026 01:38:04 -0700 Subject: [PATCH] cargo: extract lints from declared manifests only --- cargo/private/cargo_toml_info/main.rs | 13 +-- cargo/tests/cargo_toml_info/BUILD.bazel | 10 +++ .../declared_manifests_test.py | 89 +++++++++++++++++++ test/integration/cargo_lints/BUILD.bazel | 9 ++ test/integration/cargo_lints/Cargo.toml | 7 ++ test/integration/cargo_lints/MODULE.bazel | 7 ++ 6 files changed, 126 insertions(+), 9 deletions(-) create mode 100644 cargo/tests/cargo_toml_info/BUILD.bazel create mode 100644 cargo/tests/cargo_toml_info/declared_manifests_test.py create mode 100644 test/integration/cargo_lints/BUILD.bazel create mode 100644 test/integration/cargo_lints/Cargo.toml create mode 100644 test/integration/cargo_lints/MODULE.bazel diff --git a/cargo/private/cargo_toml_info/main.rs b/cargo/private/cargo_toml_info/main.rs index c0a9a06f48..67c0b76f50 100644 --- a/cargo/private/cargo_toml_info/main.rs +++ b/cargo/private/cargo_toml_info/main.rs @@ -21,18 +21,13 @@ pub fn main() -> Result<(), Box> { } = Args::try_from(std::env::args())?; let manifest_contents = std::fs::read_to_string(&manifest_toml)?; - let mut crate_manifest = Manifest::from_str(&manifest_contents)?; + let crate_manifest = Manifest::from_str(&manifest_contents)?; let mut workspace_manifest = None; - // Optionally populate the manifest with info from the parent workspace, if one is provided. + // Lint extraction only needs the declared manifests. Completing the package + // from disk would require undeclared sources and workspace members. if let Some(workspace_path) = workspace_toml { - let manifest = Manifest::from_path(&workspace_path)?; - let workspace_details = Some((&manifest, workspace_path.as_path())); - - // TODO(parkmycar): Fix cargo_toml so we inherit lints from our workspace. - // - // See: - crate_manifest.complete_from_path_and_workspace(&manifest_toml, workspace_details)?; + let manifest = Manifest::from_str(&std::fs::read_to_string(workspace_path)?)?; workspace_manifest = Some(manifest); } diff --git a/cargo/tests/cargo_toml_info/BUILD.bazel b/cargo/tests/cargo_toml_info/BUILD.bazel new file mode 100644 index 0000000000..a502e12526 --- /dev/null +++ b/cargo/tests/cargo_toml_info/BUILD.bazel @@ -0,0 +1,10 @@ +load("@rules_python//python:defs.bzl", "py_test") + +# Keep dev-only test rules out of the helper package loaded by downstream users. +py_test( + name = "declared_manifests_test", + srcs = ["declared_manifests_test.py"], + args = ["$(rlocationpath //cargo/private/cargo_toml_info)"], + data = ["//cargo/private/cargo_toml_info"], + deps = ["@rules_python//python/runfiles"], +) diff --git a/cargo/tests/cargo_toml_info/declared_manifests_test.py b/cargo/tests/cargo_toml_info/declared_manifests_test.py new file mode 100644 index 0000000000..a06ab11c68 --- /dev/null +++ b/cargo/tests/cargo_toml_info/declared_manifests_test.py @@ -0,0 +1,89 @@ +"""Lint extraction uses only the declared package and workspace manifests.""" + +import os +import subprocess +import sys +import tempfile +import unittest +from pathlib import Path + +from python.runfiles import runfiles + +WORKSPACE = """\ +[workspace] +members = ["missing-member"] + +[workspace.package] +version = "0.1.0" +readme = "missing-readme.md" + +[workspace.lints.rust] +unused = { level = "deny", priority = -1 } +unsafe_code = "forbid" + +[workspace.lints.clippy] +all = "warn" +""" + +PACKAGE = """\ +[package] +name = "example" +version.workspace = true +readme.workspace = true +edition = "2021" + +[lints] +workspace = true +""" + + +class DeclaredManifestsTest(unittest.TestCase): + def test_root_and_member_inherit_lints_without_workspace_sources(self): + lookup = runfiles.Create() + self.assertIsNotNone(lookup) + executable = lookup.Rlocation(EXTRACTOR) + self.assertIsNotNone(executable) + + with tempfile.TemporaryDirectory( + dir=os.environ.get("TEST_TMPDIR") + ) as directory: + root = Path(directory) + workspace = root / "Cargo.toml" + workspace.write_text(WORKSPACE + "\n" + PACKAGE) + member = root / "member" / "Cargo.toml" + member.parent.mkdir() + member.write_text(PACKAGE) + + for package in (workspace, member): + with self.subTest(package=package): + outputs = [ + root / f"{package.parent.name}.{group}.lints" + for group in ("rustc", "clippy", "rustdoc") + ] + result = subprocess.run( + [ + executable, + f"--manifest_toml={package.relative_to(root)}", + f"--workspace_toml={workspace.relative_to(root)}", + "lints", + *(str(path) for path in outputs), + ], + cwd=root, + capture_output=True, + text=True, + check=False, + ) + self.assertEqual(0, result.returncode, result.stderr) + self.assertEqual( + ["--deny=unused", "--forbid=unsafe_code"], + outputs[0].read_text().splitlines(), + ) + self.assertEqual( + ["--warn=clippy::all"], outputs[1].read_text().splitlines() + ) + self.assertEqual("", outputs[2].read_text()) + + +if __name__ == "__main__": + EXTRACTOR = sys.argv.pop(1) + unittest.main() diff --git a/test/integration/cargo_lints/BUILD.bazel b/test/integration/cargo_lints/BUILD.bazel new file mode 100644 index 0000000000..bc1ca19730 --- /dev/null +++ b/test/integration/cargo_lints/BUILD.bazel @@ -0,0 +1,9 @@ +load("@rules_rust//cargo:defs.bzl", "extract_cargo_lints") + +# Building this from a downstream module must not load rules_rust's dev dependencies. +# Manual check from this directory: bazel build --lockfile_mode=off //:lints +# Root-module tests cannot reproduce the pruning of dependency dev_dependencies. +extract_cargo_lints( + name = "lints", + manifest = "Cargo.toml", +) diff --git a/test/integration/cargo_lints/Cargo.toml b/test/integration/cargo_lints/Cargo.toml new file mode 100644 index 0000000000..e085c62a18 --- /dev/null +++ b/test/integration/cargo_lints/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "cargo-lints-consumer" +version = "0.1.0" +edition = "2021" + +[lints.rust] +unsafe_code = "forbid" diff --git a/test/integration/cargo_lints/MODULE.bazel b/test/integration/cargo_lints/MODULE.bazel new file mode 100644 index 0000000000..3bbe9506e4 --- /dev/null +++ b/test/integration/cargo_lints/MODULE.bazel @@ -0,0 +1,7 @@ +module(name = "cargo_lints_consumer") + +bazel_dep(name = "rules_rust", version = "0.0.0") +local_path_override( + module_name = "rules_rust", + path = "../../..", +)