Skip to content

Commit 32d58e3

Browse files
committed
feat(clippy): Introduce flag to toggle transitive aspect
1 parent f9d42ff commit 32d58e3

File tree

6 files changed

+83
-6
lines changed

6 files changed

+83
-6
lines changed

docs/rust_clippy.vm

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,10 @@ the upstream implementation of clippy, this file must be named either `.clippy.t
3030
```text
3131
build --@rules_rust//rust/settings:clippy.toml=//:clippy.toml
3232
```
33+
34+
By default, the Clippy aspect won't traverse a target's dependencies. If you'd like it to do so,
35+
toggle that behavior with the following setting:
36+
37+
```text
38+
build --@rules_rust//rust/settings:experimental_clippy_aspect_traverse_deps=True
39+
```

rust/private/clippy.bzl

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"""A module defining clippy rules"""
1616

1717
load("@bazel_skylib//lib:structs.bzl", "structs")
18+
load("@bazel_skylib//rules:common_settings.bzl", "BuildSettingInfo")
1819
load("//rust/private:common.bzl", "rust_common")
1920
load(
2021
"//rust/private:providers.bzl",
@@ -241,9 +242,26 @@ def _clippy_aspect_impl(target, ctx):
241242
toolchain = "@rules_rust//rust:toolchain_type",
242243
)
243244

244-
output_group_info = {"clippy_checks": depset([clippy_out])}
245+
traverse_deps = ctx.attr._clippy_aspect_traverse_deps[BuildSettingInfo].value
246+
247+
dep_infos = []
248+
if traverse_deps:
249+
dep_infos = [
250+
dep[ClippyInfo].output
251+
for dep in ctx.rule.attr.deps
252+
if ClippyInfo in dep
253+
]
254+
255+
output_group_info = {"clippy_checks": depset([clippy_out], transitive = dep_infos)}
245256
if clippy_diagnostics:
246-
output_group_info["clippy_output"] = depset([clippy_diagnostics])
257+
dep_diagnostics = []
258+
if traverse_deps:
259+
dep_diagnostics = [
260+
dep[OutputGroupInfo]["clippy_output"]
261+
for dep in ctx.rule.attr.deps
262+
if OutputGroupInfo in dep and "clippy_output" in dep[OutputGroupInfo]
263+
]
264+
output_group_info["clippy_output"] = depset([clippy_diagnostics], transitive = dep_diagnostics)
247265

248266
return [
249267
OutputGroupInfo(**output_group_info),
@@ -256,6 +274,7 @@ def _clippy_aspect_impl(target, ctx):
256274
# //...
257275
rust_clippy_aspect = aspect(
258276
fragments = ["cpp"],
277+
attr_aspects = ["deps"], # We traverse along the `deps` edges, but we only request the relevant files iff the _clippy_aspect_traverse_deps attr is True.
259278
attrs = {
260279
"_capture_output": attr.label(
261280
doc = "Value of the `capture_clippy_output` build setting",
@@ -278,6 +297,10 @@ rust_clippy_aspect = aspect(
278297
doc = "Value of the `clippy_output_diagnostics` build setting",
279298
default = "//rust/settings:clippy_output_diagnostics",
280299
),
300+
"_clippy_aspect_traverse_deps": attr.label(
301+
doc = "Whether the clippy aspect should traverse dependencies.",
302+
default = "//rust/settings:experimental_clippy_aspect_traverse_deps",
303+
),
281304
"_config": attr.label(
282305
doc = "The `clippy.toml` file used for configuration",
283306
allow_single_file = True,

rust/settings/BUILD.bazel

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ load(
33
":settings.bzl",
44
"always_enable_metadata_output_groups",
55
"capture_clippy_output",
6+
"clippy_aspect_traverse_deps",
67
"clippy_error_format",
78
"clippy_flag",
89
"clippy_flags",
@@ -68,6 +69,8 @@ clippy_output_diagnostics()
6869

6970
clippy_toml()
7071

72+
clippy_aspect_traverse_deps()
73+
7174
codegen_units()
7275

7376
error_format()

rust/settings/settings.bzl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,15 @@ def clippy_toml():
245245
build_setting_default = ".clippy.toml",
246246
)
247247

248+
# buildifier: disable=unnamed-macro
249+
def clippy_aspect_traverse_deps():
250+
"""This setting is used by the clippy rules. See https://bazelbuild.github.io/rules_rust/rust_clippy.html
251+
"""
252+
bool_flag(
253+
name = "experimental_clippy_aspect_traverse_deps",
254+
build_setting_default = False,
255+
)
256+
248257
# buildifier: disable=unnamed-macro
249258
def rustfmt_toml():
250259
"""This setting is used by the rustfmt rules. See https://bazelbuild.github.io/rules_rust/rust_fmt.html

test/clippy/BUILD.bazel

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,14 @@ rust_library(
9999
tags = ["noclippy"],
100100
)
101101

102+
rust_binary(
103+
name = "bad_dep",
104+
srcs = ["src/main.rs"],
105+
edition = "2018",
106+
tags = ["noclippy"],
107+
deps = [":bad_library"],
108+
)
109+
102110
rust_library(
103111
name = "bad_shared_library",
104112
srcs = ["bad_src/lib.rs"],
@@ -142,8 +150,13 @@ rust_clippy(
142150
)
143151

144152
rust_clippy(
145-
name = "bad_shared_library_clippy",
153+
name = "bad_dep_clippy",
146154
tags = ["manual"],
155+
deps = [":bad_dep"],
156+
)
157+
158+
rust_clippy(
159+
name = "bad_shared_library_clippy",
147160
deps = [":bad_shared_library"],
148161
)
149162

test/clippy/clippy_failure_tester.sh

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ NEW_WORKSPACE="${TEMP_DIR}/rules_rust_test_clippy"
2828
function check_build_result() {
2929
local ret=0
3030
echo -n "Testing ${2}... "
31+
3132
(bazel build ${@:3} //test/clippy:"${2}" &> /dev/null) || ret="$?" && true
3233
if [[ "${ret}" -ne "${1}" ]]; then
3334
>&2 echo "FAIL: Unexpected return code [saw: ${ret}, want: ${1}] building target //test/clippy:${2}"
34-
>&2 echo " Run \"bazel build //test/clippy:${2}\" to see the output"
35+
>&2 echo " Run \"bazel build //test/clippy:${2} ${@:3}\" to see the output"
3536
exit 1
3637
elif [[ $# -ge 3 ]] && [[ "${@:3}" == *"capture_clippy_output"* ]]; then
3738
# Make sure that content was written to the output file
@@ -40,10 +41,26 @@ function check_build_result() {
4041
else
4142
STATOPTS=(-c%s)
4243
fi
43-
if [[ $(stat ${STATOPTS[@]} "${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out") == 0 ]]; then
44+
# Because bad_dep_clippy itself produces an empty report, we must check its dependencies.
45+
if [[ "${2}" == "bad_dep_clippy" ]]; then
46+
# If we are traversing deps, check that the output for the dependency has been created
47+
if [[ "${@:3}" == *"clippy_aspect_traverse_deps"* ]] && [[ $(stat ${STATOPTS[@]} "${NEW_WORKSPACE}/bazel-bin/test/clippy/bad_library.clippy.out") == 0 ]]; then
48+
>&2 echo "FAIL: Output of dependency bad_library wasn't written to out file building target //test/clippy:${2}"
49+
>&2 echo " Output file: ${NEW_WORKSPACE}/bazel-bin/test/clippy/bad_library.clippy.out"
50+
>&2 echo " Run \"bazel build //test/clippy:${2} ${@:3}\" to see the output"
51+
fi
52+
if [[ -f "${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out" ]]; then
53+
>&2 echo "FAIL: Output wasn't written to out file building target //test/clippy:${2}"
54+
>&2 echo " Output file: ${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out"
55+
>&2 echo " Run \"bazel build //test/clippy:${2} ${@:3}\" to see the output"
56+
fi
57+
elif [[ $(stat ${STATOPTS[@]} "${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out") == 0 ]]; then
4458
>&2 echo "FAIL: Output wasn't written to out file building target //test/clippy:${2}"
4559
>&2 echo " Output file: ${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out"
46-
>&2 echo " Run \"bazel build //test/clippy:${2}\" to see the output"
60+
>&2 echo " Run \"bazel build //test/clippy:${2} ${@:3}\" to see the output"
61+
find -L . -name "${2%_clippy}.clippy.out"
62+
stat ${STATOPTS[@]} "${NEW_WORKSPACE}/bazel-bin/test/clippy/${2%_clippy}.clippy.out"
63+
4764
exit 1
4865
else
4966
echo "OK"
@@ -58,6 +75,7 @@ function test_all() {
5875
local -r BUILD_FAILED=1
5976
local -r CAPTURE_OUTPUT="--@rules_rust//rust/settings:capture_clippy_output=True --@rules_rust//rust/settings:error_format=json"
6077
local -r BAD_CLIPPY_TOML="--@rules_rust//rust/settings:clippy.toml=//too_many_args:clippy.toml"
78+
local -r TRAVERSE_DEPS="--@rules_rust//rust/settings:experimental_clippy_aspect_traverse_deps=True"
6179

6280
mkdir -p "${NEW_WORKSPACE}/test/clippy" && \
6381
cp -r test/clippy/* "${NEW_WORKSPACE}/test/clippy/" && \
@@ -105,6 +123,8 @@ EOF
105123
check_build_result $BUILD_OK ok_proc_macro_clippy
106124
check_build_result $BUILD_FAILED bad_binary_clippy
107125
check_build_result $BUILD_FAILED bad_library_clippy
126+
check_build_result $BUILD_OK bad_dep_clippy
127+
check_build_result $BUILD_FAILED bad_dep_clippy $TRAVERSE_DEPS
108128
check_build_result $BUILD_FAILED bad_shared_library_clippy
109129
check_build_result $BUILD_FAILED bad_static_library_clippy
110130
check_build_result $BUILD_FAILED bad_test_clippy
@@ -114,6 +134,8 @@ EOF
114134
# should succeed.
115135
check_build_result $BUILD_OK bad_binary_clippy $CAPTURE_OUTPUT
116136
check_build_result $BUILD_OK bad_library_clippy $CAPTURE_OUTPUT
137+
check_build_result $BUILD_OK bad_dep_clippy $CAPTURE_OUTPUT
138+
check_build_result $BUILD_OK bad_dep_clippy $CAPTURE_OUTPUT $TRAVERSE_DEPS
117139
check_build_result $BUILD_OK bad_shared_library_clippy $CAPTURE_OUTPUT
118140
check_build_result $BUILD_OK bad_static_library_clippy $CAPTURE_OUTPUT
119141
check_build_result $BUILD_OK bad_test_clippy $CAPTURE_OUTPUT

0 commit comments

Comments
 (0)