Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
8.6.0
2 changes: 2 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ on:
jobs:
bazel-test:
runs-on: ubuntu-latest
env:
USE_BAZEL_VERSION: 8.6.0

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The workflow currently builds //:sample_clippy with the strict and relaxed Clippy configs, but it does not appear to exercise the new lint-profiles/{strict,relaxed}/Cargo.toml files or rustc-lints/flags.bzl.

This matters because the PR currently seems to contain a concrete Cargo/Bazel drift bug: lint-profiles/relaxed/Cargo.toml sets unsafe_op_in_unsafe_fn = "warn", while RELAXED_RUSTC_FLAGS uses -Dunsafe_op_in_unsafe_fn.

Could we add at least a small check that compares the Cargo [lints.rust] levels against STRICT_RUSTC_FLAGS and RELAXED_RUSTC_FLAGS? That would catch the current drift without requiring a large integration test.

It would also be useful, either here or in follow-up issues, to verify that the strict/relaxed profiles differ only where intended and that the exported Bazel labels for the new files are loadable by a consumer workspace. If this PR grows Bazel-native Clippy lint support via rust_lint_config, then a small consumer probe for representative Clippy denies such as unwrap_used, expect_used, panic, print_stdout, and dbg_macro would be a good regression test for that path too.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tools/check_lint_sync.py, run as a CI step, which translates each Cargo [lints.rust] table into rustc flags and diffs them against STRICT_RUSTC_FLAGS/RELAXED_RUSTC_FLAGS. It catches exactly the -D vs -W drift you found (verified locally: passes now, fails if I reintroduce the bug). A full Bazel-native Clippy-deny regression test (unwrap_used, expect_used, etc. via rust_lint_config) is tracked as a follow-up.

steps:
- uses: actions/checkout@v4
- name: Install Bazelisk
Expand Down
4,460 changes: 4,394 additions & 66 deletions MODULE.bazel.lock

Large diffs are not rendered by default.

53 changes: 45 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ Centralized Rust linting and formatting policies for the Eclipse Safe Open Vehic
- Distribute those policies as a Bazel module (`score_rust_policies`) so bzlmod users can depend on them directly.
- Keep tooling configurations (e.g., Clippy, rustfmt) versioned and auditable in one place.

## Clippy policy levels
- `clippy/strict/clippy.toml`: ASIL-B–oriented settings for safety-critical code (enables `pedantic`/`nursery`, disallows `panic`/`unwrap`/`expect`, forbids debug/print macros, and enforces size/complexity thresholds).
## Rust lint policy levels
- `clippy/strict/clippy.toml`: ASIL-B–oriented settings for safety-critical code (enables `pedantic`/`nursery`, checks private items, disallows `panic`/`unwrap`/`expect`, forbids debug/print macros, and enforces size/complexity thresholds).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the strict-policy intent described here is good, but I do not think clippy/strict/clippy.toml by itself enforces most of the lint levels named in this sentence. The file currently only seems to configure Clippy options:

msrv = "1.90.0"
check-private-items = true

Lints such as unwrap_used, expect_used, panic, dbg_macro, and print_stdout need to be enforced through Cargo [lints.clippy], crate attributes, Clippy command-line flags, or Bazel rules_rust lint configuration.

Could we either narrow the README wording to say that clippy.toml configures Clippy options, not these lint levels, or add a Bazel-native lint config for the claimed strict/relaxed Clippy levels?

For the Bazel-native path, rules_rust appears to support this through rust_lint_config(...) plus the lint_config attribute on Rust targets. I tested that locally with a temporary consumer: using only the strict clippy.toml, probes containing dynamic unwrap(), expect(), panic!(), println!, and dbg! passed under bazel build --config=clippy-strict. When I attached a rust_lint_config with the corresponding Clippy lint levels via lint_config, rust_clippy rejected the same probes.

If adding Bazel-native strict/relaxed lint targets is more than you want in this PR, I think documenting the current limitation here and opening a follow-up issue for rust_lint_config support would also resolve the immediate documentation mismatch and let you land this one.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, the wording overstated it. Reworded the "Rust lint policy levels" section to make clear that clippy.toml only configures Clippy options (msrv, check-private-items, thresholds), while the actual lint levels (unwrap_used, panic, print_stdout, etc.) come from the Cargo [lints.clippy]/[lints.rust] tables in lint-profiles/, or from rustc-lints/flags.bzl for Bazel-first repos. Bazel-native rust_lint_config enforcement is a good follow-up; tracking it as a separate issue rather than expanding this PR.

- `clippy/relaxed/clippy.toml`: For tooling, generators, and tests where controlled panics/unwraps and debug printing are acceptable. Still forbids `todo` and `unimplemented`.
- `lint-profiles/strict/Cargo.toml` / `lint-profiles/relaxed/Cargo.toml`: Corresponding `[lints.rust]`, `[lints.clippy]`, and `[profile.release]` settings for use directly in a project's `Cargo.toml`. The main behavioral split today is that `strict` keeps `unsafe_op_in_unsafe_fn = "deny"`, while `relaxed` downgrades it to `warn`; the rest is currently aligned. See the [SCORE Rust Coding Guidelines](https://eclipse-score.github.io/score/contribute/development/rust/coding_guidelines.html) for the full rationale and coverage matrix.

## How to use Clippy in consumers
## How to use lint policies in consumers
- Wire configs in your repo’s `.bazelrc` (mirrors `tests/.bazelrc`):
```
build:clippy-strict --@rules_rust//rust/settings:clippy.toml=@score_rust_policies//clippy/strict:clippy.toml

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README says this mirrors tests/.bazelrc, but the config names differ:

  • README uses build:clippy-strict and build:clippy-relaxed.
  • tests/.bazelrc uses build:strict and build:relaxed.
  • The validation instructions later use bazel build --config=strict and --config=relaxed.

Could we make the names consistent? Otherwise a consumer copying the README cannot compare directly against the test workspace, and the example commands do not match the example config definitions.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unified everything on clippy-strict/clippy-relaxed: tests/.bazelrc, the workflow, and the README validation commands now use the same names, so a consumer can copy directly from the README and compare against the test workspace.

Expand Down Expand Up @@ -38,6 +39,40 @@ Centralized Rust linting and formatting policies for the Eclipse Safe Open Vehic
```
Then run `bazel build --config=clippy-strict //:clippy` (or `--config=clippy-relaxed`).

## Applying rustc lints in a Bazel-first repo
The `[lints.rust]` table in `lint-profiles/{strict,relaxed}/Cargo.toml` is a Cargo
feature and is **not** read by `rules_rust`. For Bazel-only consumers (e.g.
`baselibs_rust`), the same rustc lints are exported as ready-to-use flag lists in
[rustc-lints/flags.bzl](rustc-lints/flags.bzl), so they don't have to be redefined.

- Load the shared flag list and apply it per target:
```starlark
load("@score_rust_policies//rustc-lints:flags.bzl", "STRICT_RUSTC_FLAGS")

rust_library(
name = "my_lib",
srcs = [...],
rustc_flags = STRICT_RUSTC_FLAGS, # or RELAXED_RUSTC_FLAGS
)
```
- To apply the same lints to every target via a `.bazelrc` config instead, use
`@rules_rust//rust/settings:extra_rustc_flags`. Note: a `.bazelrc` cannot `load`
a `.bzl`, so the flags must be repeated literally there:
```
build:rustc-strict --@rules_rust//rust/settings:extra_rustc_flags=-Wunused,-Dunsafe_op_in_unsafe_fn,-Wmissing_abi,-Wunreachable_pub,-Wmissing_docs,-Wunused_results,-Wlet_underscore_drop,-Wnon_exhaustive_omitted_patterns,-Welided_lifetimes_in_paths,-Wexplicit_outlives_requirements,-Wmacro_use_extern_crate,-Wmeta_variable_misuse,-Wnon_local_definitions,-Wredundant_lifetimes,-Wsingle_use_lifetimes,-Wtrivial_numeric_casts,-Wunit_bindings,-Wunnameable_types,-Wvariant_size_differences
```
- Combine with the matching Clippy profile when building:
```
bazel build --config=clippy-strict --config=rustc-strict //src/...
```

Notes:
- The flag lists in [rustc-lints/flags.bzl](rustc-lints/flags.bzl) mirror the
`[lints.rust]` tables in lint-profiles; keep both in sync (there is no automatic
translation from `[lints.rust]` to rustc flags).
- Loading the constant (per target) avoids duplicating the flags; the global
`.bazelrc` path cannot read the `.bzl` and therefore repeats them.

## Local validation
- From `tests/` (consumer workspace with a local_path_override) run:
- `bazel build --config=strict //:sample_clippy`
Expand All @@ -47,7 +82,7 @@ Centralized Rust linting and formatting policies for the Eclipse Safe Open Vehic
## Using with Bazel (bzlmod)
- Add to your `MODULE.bazel`:
```
bazel_dep(name = "score_rust_policies", version = "0.0.4")
bazel_dep(name = "score_rust_policies", version = "<latest-version>")
```
- During local development you can pin a checkout with:
```
Expand All @@ -57,8 +92,8 @@ Centralized Rust linting and formatting policies for the Eclipse Safe Open Vehic
)
```
- Reference policy files in Bazel targets with:
- `@score_rust_policies//clippy:clippy.toml` for safety components.
- `@score_rust_policies//clippy:clippy_relaxed.toml` for tooling/tests.
- `@score_rust_policies//clippy/strict:clippy.toml` for safety components.
- `@score_rust_policies//clippy/relaxed:clippy.toml` for tooling/tests.
- When running Clippy directly, pass the config you need: `cargo clippy --config-path path/to/clippy/clippy.toml`.

## Repository layout
Expand All @@ -67,9 +102,11 @@ Centralized Rust linting and formatting policies for the Eclipse Safe Open Vehic
- `LICENSE.md`: Apache License 2.0.
- `CONTRIBUTION.md`: contribution process and links to S-CORE guidelines.
- `.gitignore`: common ignores for Bazel and development tooling.
- `clippy/`: Clippy configs exported as `@score_rust_policies//clippy/{strict,relaxed}:clippy.toml`.
- `clippy/`: Clippy-only configs exported as `@score_rust_policies//clippy/{strict,relaxed}:clippy.toml`.
- `lint-profiles/`: Cargo lint profiles exported as `@score_rust_policies//lint-profiles/{strict,relaxed}:Cargo.toml`.
- `rustc-lints/`: rustc lint flag lists for Bazel consumers, exported as `@score_rust_policies//rustc-lints:flags.bzl` (`STRICT_RUSTC_FLAGS` / `RELAXED_RUSTC_FLAGS`).
- `tests/`: consumer workspace that depends on this module via `local_path_override` and runs Clippy with strict/relaxed configs.
- (planned) `rustfmt/`: rustfmt defaults when added.
- `rustfmt/`: rustfmt defaults.

## Contributing
See `CONTRIBUTION.md` for how to propose and review policy changes. All contributions require ECA/DCO sign-off and follow the Eclipse Foundation project handbook.
Expand Down
3 changes: 2 additions & 1 deletion clippy/strict/clippy.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# clippy.toml - ASIL-B profile, compatible with your Clippy version
# License: Apache-2.0 (see LICENSE.md).

msrv = "1.90.0"
msrv = "1.90.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we clarify the intended MSRV here? clippy/strict/clippy.toml sets msrv = "1.90.0", while the test workspace pins Rust 1.85.0 in tests/MODULE.bazel.

My understanding is that Clippy msrv normally represents the minimum Rust version the codebase supports. If the policy MSRV and the tested toolchain intentionally differ, it would help to document that relationship so consumers know which compiler version the policy is meant to target.

Could we either align these values, lower the Clippy msrv to the currently tested toolchain, or add a short note explaining why they intentionally differ? A broader compiler-version matrix could also be tracked as a follow-up issue if that is outside the intended scope of this PR.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented the relationship in both clippy.toml files: msrv is the policy's target minimum supported Rust version and is intentionally independent of the toolchain pinned in tests/MODULE.bazel (1.85.0), which only validates config wiring. Happy to align them to a single value instead if you'd prefer — let me know which direction.

check-private-items = true
14 changes: 14 additions & 0 deletions lint-profiles/relaxed/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

exports_files(["Cargo.toml"], visibility = ["//visibility:public"])
93 changes: 93 additions & 0 deletions lint-profiles/relaxed/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# Cargo.toml lint profile – safety-oriented practical baseline
# License: Apache-2.0 (see LICENSE.md).
#
# Background and rationale: https://eclipse-score.github.io/score/contribute/development/rust/coding_guidelines.html
#
# Copy the [lints.rust], [lints.clippy], and [profile.release] sections below
# into your project's Cargo.toml to adopt the SCORE practical baseline.

# Rust compiler lints (rustc)
[lints.rust]
# Relaxed baseline keeps the lint visible but does not fail the build.
unsafe_op_in_unsafe_fn = "warn"
missing_abi = "warn"
unreachable_pub = "warn"
missing_docs = "warn"
unused_results = "warn"
let_underscore_drop = "warn"
non_exhaustive_omitted_patterns = "warn"

# Rustc lints recommended to evaluate as warn
elided_lifetimes_in_paths = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
meta_variable_misuse = "warn"
non_local_definitions = "warn"
redundant_lifetimes = "warn"
single_use_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unnameable_types = "warn"
variant_size_differences = "warn"

# Edition and compatibility lint groups (baseline intent)
# Enable rust-<YEAR>-* and keyword-idents-<YEAR> lints supported by the
# pinned compiler version.
unused = { level = "warn", priority = -1 }

# Future/availability-dependent rustc lints (enable when available in
# the selected toolchain)
# must_not_suspend = "warn"
# fuzzy_provenance_casts = "warn"
# lossy_provenance_casts = "warn"

# Clippy lints
[lints.clippy]
# Warn
as_underscore = "warn"
cast_lossless = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"
cast_sign_loss = "warn"
cast_ptr_alignment = "warn"
exit = "warn"
format_push_string = "warn"
infinite_loop = "warn"
iter_over_hash_type = "warn"
invalid_upcast_comparisons = "warn"
lossy_float_literal = "warn"
missing_errors_doc = "warn"
missing_docs_in_private_items = "warn"
panic_in_result_fn = "warn"
ptr_cast_constness = "warn"
ref_as_ptr = "warn"
transmute_ptr_to_ptr = "warn"
redundant_type_annotations = "warn"
shadow_reuse = "warn"
shadow_unrelated = "warn"
try_err = "warn"
wildcard_enum_match_arm = "warn"

# Deny

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relaxed profile still denies several lints, including missing_panics_doc, undocumented_unsafe_blocks, wildcard_imports, and declare_interior_mutable_const. That may be exactly the right SCORE policy, but the README describes relaxed as suitable for tooling, generators, and tests where controlled panics/unwraps/debug printing are acceptable.

Could we document the intended boundary a bit more explicitly? In particular, it would help to say which safety/security lints remain hard errors even in the relaxed profile, and which lints are intentionally downgraded for tooling/test code.

This does not necessarily need to block this PR if the current deny list is intentional. If the boundary is still being worked out, a short README note plus a follow-up issue for the full strict/relaxed policy matrix would be enough from my perspective.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a "Relaxed profile boundary" section to the README spelling out which safety/doc lints stay hard errors even in relaxed (missing_panics_doc, undocumented_unsafe_blocks, wildcard_imports, declare_interior_mutable_const), and that unsafe_op_in_unsafe_fn is the only rustc level that differs between strict and relaxed today. The full policy matrix is deferred to the coding-guidelines doc.

as_ptr_cast_mut = "deny"
let_underscore_must_use = "deny"
missing_panics_doc = "deny"
undocumented_unsafe_blocks = "deny"
wildcard_imports = "deny"
declare_interior_mutable_const = "deny"

# Allow
shadow_same = "allow"
implicit_return = "allow"
allow_attributes = "allow"

# Evaluate for helpfulness
panicking_overflow_checks = "warn"
unwrap_used = "warn"

# Recommended against because too coarse
as_conversions = "allow"

# Release profile requirement
[profile.release]
overflow-checks = true # Required for safety-oriented integer checks
14 changes: 14 additions & 0 deletions lint-profiles/strict/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

exports_files(["Cargo.toml"], visibility = ["//visibility:public"])
85 changes: 85 additions & 0 deletions lint-profiles/strict/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Cargo.toml lint profile – safety-oriented strict/ASIL variant
# License: Apache-2.0 (see LICENSE.md).
#
# Background and rationale: https://eclipse-score.github.io/score/contribute/development/rust/coding_guidelines.html
#
# Copy the [lints.rust], [lints.clippy], and [profile.release] sections below
# into your project's Cargo.toml to adopt the SCORE strict/ASIL baseline.
# Also enable "check-private-items = true" in clippy.toml for this profile.

# Rust compiler lints (strict)
[lints.rust]
unsafe_op_in_unsafe_fn = "deny"
missing_abi = "warn"
unreachable_pub = "warn"
missing_docs = "warn"
unused_results = "warn"
let_underscore_drop = "warn"
non_exhaustive_omitted_patterns = "warn"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

non_exhaustive_omitted_patterns = "warn" currently produces an unknown lint warning on stable Rust in my probes:

warning: unknown lint: `non_exhaustive_omitted_patterns`
  = note: the `non_exhaustive_omitted_patterns` lint is unstable

The same warning appears through the Bazel rustc flag list (-Wnon_exhaustive_omitted_patterns) with the PR's rules_rust toolchain. That means adopting the policy can inject a warning before user code is considered, which seems problematic for a safety-oriented policy that may expect warning-free builds.

Could we gate this lint by supported toolchain, remove it from the default stable baseline until it is available without #![feature(non_exhaustive_omitted_patterns_lint)], or document that this part of the profile requires nightly/unstable lint support? Given the surrounding guidance appears to favor stable toolchains, I would suggest not enabling this lint in the default stable profiles.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed it shouldn't inject a warning into a warning-free safety build on stable. Commented it out in both Cargo profiles and removed -Wnon_exhaustive_omitted_patterns from both flag lists, with a note to re-enable only on nightly (#![feature(non_exhaustive_omitted_patterns_lint)]).


# Rustc lints recommended to evaluate as warn
elided_lifetimes_in_paths = "warn"
explicit_outlives_requirements = "warn"
macro_use_extern_crate = "warn"
meta_variable_misuse = "warn"
non_local_definitions = "warn"
redundant_lifetimes = "warn"
single_use_lifetimes = "warn"
trivial_numeric_casts = "warn"
unit_bindings = "warn"
unnameable_types = "warn"
variant_size_differences = "warn"

# Edition and compatibility lint groups (baseline intent)
unused = { level = "warn", priority = -1 }

# Clippy lints (strict)
[lints.clippy]
# Warn
as_underscore = "warn"
cast_lossless = "warn"
cast_possible_truncation = "warn"
cast_possible_wrap = "warn"
cast_sign_loss = "warn"
cast_ptr_alignment = "warn"
exit = "warn"
format_push_string = "warn"
infinite_loop = "warn"
iter_over_hash_type = "warn"
invalid_upcast_comparisons = "warn"
lossy_float_literal = "warn"
missing_errors_doc = "warn"
missing_docs_in_private_items = "warn"
panic_in_result_fn = "warn"
ptr_cast_constness = "warn"
ref_as_ptr = "warn"
transmute_ptr_to_ptr = "warn"
redundant_type_annotations = "warn"
shadow_reuse = "warn"
shadow_unrelated = "warn"
try_err = "warn"
wildcard_enum_match_arm = "warn"

# Deny
as_ptr_cast_mut = "deny"
let_underscore_must_use = "deny"
missing_panics_doc = "deny"
undocumented_unsafe_blocks = "deny"
wildcard_imports = "deny"
declare_interior_mutable_const = "deny"

# Allow
shadow_same = "allow"
implicit_return = "allow"
allow_attributes = "allow"

# Evaluate for helpfulness
panicking_overflow_checks = "warn"
unwrap_used = "warn"

# Recommended against because too coarse
as_conversions = "allow"

# Release profile requirement
[profile.release]
overflow-checks = true
14 changes: 14 additions & 0 deletions rustc-lints/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# *******************************************************************************
# Copyright (c) 2026 Contributors to the Eclipse Foundation
#
# See the NOTICE file(s) distributed with this work for additional
# information regarding copyright ownership.
#
# This program and the accompanying materials are made available under the
# terms of the Apache License Version 2.0 which is available at
# https://www.apache.org/licenses/LICENSE-2.0
#
# SPDX-License-Identifier: Apache-2.0
# *******************************************************************************

exports_files(["flags.bzl"], visibility = ["//visibility:public"])
Loading
Loading