Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
66 changes: 66 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,17 @@ name: Tests

on:
pull_request:
types: [opened, reopened, synchronize]
push:
branches:
- main
merge_group:
types: [checks_requested]
workflow_call:

permissions:
contents: read
actions: write

jobs:
test-sanitizers:
Expand Down Expand Up @@ -147,3 +152,64 @@ jobs:
- name: Verify ${{ matrix.label }} sanitizer catches bugs (negative)
working-directory: tests
run: bazel test --config=${{ matrix.config }} //:${{ matrix.runtime_target }} --verbose_failures

test-warnings:
name: Warnings feature (${{ matrix.label }})
runs-on: ubuntu-24.04

strategy:
fail-fast: false
matrix:
include:
- label: minimal_warnings
config: feature_only_gcc_minimal_warnings
violation_target: minimal_warnings_violation
expected_diagnostic: "-W(error=)?cast-qual"
- label: strict_warnings
config: feature_only_gcc_strict_warnings
violation_target: strict_warnings_violation
expected_diagnostic: "-W(error=)?vla"
- label: all_wall_warnings
config: feature_only_gcc_all_wall_warnings
violation_target: all_wall_warnings_violation
expected_diagnostic: "-W(error=)?unused-variable"

steps:
- name: Checkout
uses: actions/checkout@v6

Comment on lines +178 to +180
- name: Setup Bazel
uses: bazel-contrib/setup-bazel@0.18.0
with:
bazelisk-cache: true
cache-save: ${{ github.event_name == 'push' }}

- name: Verify ${{ matrix.label }} + warnings_as_errors has no false positives on clean code
working-directory: tests
run: bazel test --config=${{ matrix.config }} //:warnings_positive_test --verbose_failures

# This is a compile-time (build) failure, not a runnable test, so we invert
# the exit code here instead of relying on `bazel test` (which doesn't
# apply to cc_library targets). We also grep the log for the specific
# diagnostic each violation file documents, so an unrelated build failure
# (bad flag, missing toolchain, typo, ...) isn't mistaken for a pass.
- name: Verify ${{ matrix.label }} catches its documented violation (negative)
working-directory: tests
run: |
set +e
bazel build --config=${{ matrix.config }} //:${{ matrix.violation_target }} --verbose_failures > build.log 2>&1
STATUS=$?
set -e
cat build.log

if [ "$STATUS" -eq 0 ]; then
echo "::error::Expected //:${{ matrix.violation_target }} to fail to build under --config=${{ matrix.config }}, but it succeeded"
exit 1
fi

if ! grep -qE -- '${{ matrix.expected_diagnostic }}' build.log; then
echo "::error:://:${{ matrix.violation_target }} failed to build, but not because of the expected '${{ matrix.expected_diagnostic }}' diagnostic — see log above for the actual cause"
exit 1
fi


70 changes: 70 additions & 0 deletions docs/warnings.md
Original file line number Diff line number Diff line change
Expand Up @@ -327,3 +327,73 @@ enabled to a hard compile error.
|---|---|---|
| `-Werror` | Linux & QNX | Turn every currently-enabled warning into a compile error, so a build cannot succeed while warnings remain. |
| `-Wno-error=deprecated-declarations` | Linux only | Exempt deprecated-declaration warnings from the `-Werror` escalation above — deprecations are advisory and shouldn't block a build. |

---

## Testing

The [`tests/`](../tests) module exercises the warnings features on GCC. It
registers a dedicated toolchain (`score_gcc_toolchain_15`, GCC 15.3.0) in
[`tests/MODULE.bazel`](../tests/MODULE.bazel) with `minimal_warnings`,
`strict_warnings`, `all_wall_warnings`, and `warnings_as_errors` added to
`extra_known_features` — this is the toolchain that must be used to test
these features; it is **not** the same toolchain used for the sanitizer
tests (`score_gcc_x86_64_toolchain_fi`).

[`tests/.bazelrc`](../tests/.bazelrc) defines one `--config` per severity
level, each pinning that toolchain and bundling `warnings_as_errors` so a
false positive turns into a build failure instead of a silent warning:

| Config | Feature enabled |
|---|---|
| `feature_only_gcc_minimal_warnings` | `minimal_warnings` + `warnings_as_errors` |
| `feature_only_gcc_strict_warnings` | `strict_warnings` + `warnings_as_errors` |
| `feature_only_gcc_all_wall_warnings` | `all_wall_warnings` + `warnings_as_errors` |

### Positive test — no false positives

[`tests/warnings/positive_test.cpp`](../tests/warnings/positive_test.cpp) is
idiomatic code that must compile clean at every severity level. Run it as a
normal test, e.g.:

```bash
cd tests
bazel test --config=feature_only_gcc_minimal_warnings //:warnings_positive_test
```

### Negative targets — each severity actually catches its violation

`minimal_warnings_violation`, `strict_warnings_violation`, and
`all_wall_warnings_violation` are `cc_library` targets tagged `manual`,
each containing one documented, intentional violation (see the header
comment in the corresponding file:
[`tests/warnings/minimal_violation.cpp`](../tests/warnings/minimal_violation.cpp),
[`tests/warnings/strict_violation.cpp`](../tests/warnings/strict_violation.cpp),
[`tests/warnings/all_wall_violation.cpp`](../tests/warnings/all_wall_violation.cpp)).
They must **fail to build** under their matching config:

```bash
cd tests
bazel build --config=feature_only_gcc_minimal_warnings //:minimal_warnings_violation
```

These are `cc_library` targets, not tests — `bazel test` doesn't apply to
them (there's no runnable action), so CI invokes `bazel build` directly and
inverts the exit code (see `test-warnings` in
[`.github/workflows/tests.yml`](../.github/workflows/tests.yml)):

```bash
if bazel build --config=feature_only_gcc_minimal_warnings //:minimal_warnings_violation; then
echo "expected this build to fail, but it succeeded" >&2
exit 1
fi
```

To confirm a violation file builds clean *without* the warnings feature
enabled (i.e. the failure above really comes from the feature, not from
some other compiler default), build it against the plain toolchain:

```bash
cd tests
bazel build --extra_toolchains=@score_gcc_toolchain_15//:x86_64-linux-gcc_15.3.0 //:minimal_warnings_violation
```
24 changes: 24 additions & 0 deletions tests/.bazelrc
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,27 @@ test:feature_only_gcc_lsan --test_env=LSAN_OPTIONS=exitcode=55
build:feature_only_gcc_tsan --extra_toolchains=@score_gcc_x86_64_toolchain_fi//:x86_64-linux
build:feature_only_gcc_tsan --features=score_tsan
test:feature_only_gcc_tsan --test_env=TSAN_OPTIONS=exitcode=55:halt_on_error=1

# Warnings features (warnings/gcc) are GCC-only, so these always pin the GCC
# toolchain. Each config also enables warnings_as_errors so a false positive at
# that severity level turns into a build failure instead of a silent warning.
# Shared configuration for simple test execution
build:shared --incompatible_strict_action_env
build:shared --sandbox_writable_path=/var/tmp

build:x86_64-linux-gcc15 --config=shared
build:x86_64-linux-gcc15 --host_platform=@score_bazel_platforms//:x86_64-linux-gcc_15.3.0-posix
build:x86_64-linux-gcc15 --extra_toolchains=@score_gcc_toolchain_15//:x86_64-linux-gcc_15.3.0
build:x86_64-linux-gcc15 --host_features=use_pthread

build:feature_only_gcc_minimal_warnings --config=x86_64-linux-gcc15
build:feature_only_gcc_minimal_warnings --features=minimal_warnings
build:feature_only_gcc_minimal_warnings --features=warnings_as_errors

build:feature_only_gcc_strict_warnings --config=x86_64-linux-gcc15
build:feature_only_gcc_strict_warnings --features=strict_warnings
build:feature_only_gcc_strict_warnings --features=warnings_as_errors

build:feature_only_gcc_all_wall_warnings --config=x86_64-linux-gcc15
build:feature_only_gcc_all_wall_warnings --features=all_wall_warnings
build:feature_only_gcc_all_wall_warnings --features=warnings_as_errors
31 changes: 31 additions & 0 deletions tests/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,35 @@ sh_test(
target_compatible_with = ["@score_cpp_policies//sanitizers/constraints:only_ubsan"],
)

# ==============================================================================
# Warnings feature tests (warnings/gcc) - GCC-only, see .bazelrc feature_only_gcc_*_warnings
# ==============================================================================

# Must build clean at every severity level, even combined with warnings_as_errors.
# Run with e.g.: bazel test --config=feature_only_gcc_all_wall_warnings :warnings_positive_test
cc_test(
name = "warnings_positive_test",
srcs = ["warnings/positive_test.cpp"],
)

# Documented, intentional violations - one per severity level. Not built by
# default (tags = ["manual"]); see each file's header comment to verify manually.
cc_library(
name = "minimal_warnings_violation",
srcs = ["warnings/minimal_violation.cpp"],
tags = ["manual"],
)

cc_library(
name = "strict_warnings_violation",
srcs = ["warnings/strict_violation.cpp"],
tags = ["manual"],
)

cc_library(
name = "all_wall_warnings_violation",
srcs = ["warnings/all_wall_violation.cpp"],
tags = ["manual"],
)

# =======================================================================
24 changes: 16 additions & 8 deletions tests/MODULE.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,16 @@ module(name = "score_cpp_policies_tests")
bazel_dep(name = "googletest", version = "1.17.0.bcr.2")
bazel_dep(name = "rules_cc", version = "0.2.17")
bazel_dep(name = "toolchains_llvm", version = "1.7.0")
bazel_dep(name = "score_bazel_platforms", version = "1.0.0")
bazel_dep(name = "score_cpp_policies")
local_path_override(
module_name = "score_cpp_policies",
path = "..",
)

bazel_dep(name = "score_bazel_cpp_toolchains")
git_override(
module_name = "score_bazel_cpp_toolchains",
commit = "942559c7dcdfa72d53343fcf048cade00d4accb8",
remote = "https://github.com/eclipse-score/bazel_cpp_toolchains",
)

# --- GCC toolchain ---

bazel_dep(name = "score_bazel_cpp_toolchains", version = "1.0.2")
gcc = use_extension("@score_bazel_cpp_toolchains//extensions:gcc.bzl", "gcc")
gcc.toolchain(
name = "score_gcc_x86_64_toolchain_fi",
Expand All @@ -48,7 +43,20 @@ gcc.toolchain(
use_default_package = True,
version = "12.2.0",
)
use_repo(gcc, "score_gcc_x86_64_toolchain_fi")
gcc.toolchain(
name = "score_gcc_toolchain_15",
extra_known_features = [
"@score_cpp_policies//warnings/gcc/features:minimal_warnings",
"@score_cpp_policies//warnings/gcc/features:strict_warnings",
"@score_cpp_policies//warnings/gcc/features:all_wall_warnings",
"@score_cpp_policies//warnings/gcc/features:warnings_as_errors",
],
target_cpu = "x86_64",
target_os = "linux",
use_default_package = True,
version = "15.3.0",
)
use_repo(gcc, "score_gcc_x86_64_toolchain_fi", "score_gcc_toolchain_15")

# --- Clang toolchain ---

Expand Down
27 changes: 27 additions & 0 deletions tests/warnings/all_wall_violation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// *******************************************************************************
// 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
// *******************************************************************************

// Intentional -Wunused-variable violation (part of `all_wall_warnings`).
// This file exists to document what all_wall_warnings catches.
// It must NOT be included in the regular test build (tags = ["manual"]).
//
// Verify with:
// bazel build --config=feature_only_gcc_all_wall_warnings :all_wall_warnings_violation
// -> fails (warnings_as_errors is bundled into the config)
// bazel build --extra_toolchains=@score_gcc_toolchain_15//:x86_64-linux-gcc_15.3.0 :all_wall_warnings_violation
// -> succeeds silently (no warnings feature enabled)

int compute() {
int unused_value = 42; // -Wunused-variable
return 0;
}
27 changes: 27 additions & 0 deletions tests/warnings/minimal_violation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// *******************************************************************************
// 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
// *******************************************************************************

// Intentional -Wcast-qual violation (part of `minimal_warnings`).
// This file exists to document what minimal_warnings catches.
// It must NOT be included in the regular test build (tags = ["manual"]).
//
// Verify with:
// bazel build --config=feature_only_gcc_minimal_warnings :minimal_warnings_violation
// -> fails (warnings_as_errors is bundled into the config)
// bazel build --extra_toolchains=@score_gcc_toolchain_15//:x86_64-linux-gcc_15.3.0 :minimal_warnings_violation
// -> succeeds silently (no warnings feature enabled)

int strip_const(const int* p) {
int* mutable_p = (int*)p; // -Wcast-qual: C-style cast discards `const`
return *mutable_p;
}
31 changes: 31 additions & 0 deletions tests/warnings/positive_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// *******************************************************************************
// 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
// *******************************************************************************

// Must compile clean under minimal/strict/all_wall_warnings, even combined with
// warnings_as_errors (-Werror) — proves the policy has no false positives on
// idiomatic code. See feature_only_gcc_{minimal,strict,all_wall}_warnings in .bazelrc.

#include <cstdint>

namespace {

std::uint32_t add(std::uint32_t a, std::uint32_t b) {
return a + b;
}

} // namespace

int main() {
const std::uint32_t result = add(2, 3);
return result == 5 ? 0 : 1;
}
32 changes: 32 additions & 0 deletions tests/warnings/strict_violation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// *******************************************************************************
// 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
// *******************************************************************************

// Intentional -Wvla violation (part of `strict_warnings`).
// This file exists to document what strict_warnings catches.
// It must NOT be included in the regular test build (tags = ["manual"]).
//
// Verify with:
// bazel build --config=feature_only_gcc_strict_warnings :strict_warnings_violation
// -> fails (warnings_as_errors is bundled into the config)
// bazel build --extra_toolchains=@score_gcc_toolchain_15//:x86_64-linux-gcc_15.3.0 :strict_warnings_violation
// -> succeeds silently (no warnings feature enabled)

int sum_first_n(int n) {
int values[n]; // -Wvla: variable-length array
int total = 0;
for (int i = 0; i < n; ++i) {
values[i] = i;
total += values[i];
}
return total;
}
Loading