Skip to content

Commit 49906eb

Browse files
authored
Update clippy and rustfmt aspects to require CrateInfo providers (#1772)
* Update clippy and rustfmt aspects to require CrateInfo providers * Updated rustfmt rules to work with `rust_shared/static_library` * Regenerate documentation
1 parent 8556420 commit 49906eb

File tree

13 files changed

+226
-99
lines changed

13 files changed

+226
-99
lines changed

.bazelci/presubmit.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ tasks:
292292
name: Negative Rustfmt Tests
293293
platform: ubuntu2004
294294
run_targets:
295-
- "//test/rustfmt:test_runner"
295+
- "//test/rustfmt:rustfmt_integration_test_suite.test_runner"
296296
rust_analyzer_tests:
297297
name: Rust-Analyzer Tests
298298
platform: ubuntu2004

docs/flatten.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2082,7 +2082,6 @@ This aspect is used to gather information about a crate for use in rustfmt and p
20822082

20832083
Output Groups:
20842084

2085-
- `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
20862085
- `rustfmt_checks`: Executes `rustfmt --check` on the specified target.
20872086

20882087
The build setting `@rules_rust//:rustfmt.toml` is used to control the Rustfmt [configuration settings][cs]

docs/rust_fmt.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ This aspect is used to gather information about a crate for use in rustfmt and p
9797

9898
Output Groups:
9999

100-
- `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
101100
- `rustfmt_checks`: Executes `rustfmt --check` on the specified target.
102101

103102
The build setting `@rules_rust//:rustfmt.toml` is used to control the Rustfmt [configuration settings][cs]

rust/private/clippy.bzl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ clippy_flags = rule(
4545
build_setting = config.string_list(flag = True),
4646
)
4747

48-
def _get_clippy_ready_crate_info(target, aspect_ctx):
48+
def _get_clippy_ready_crate_info(target, aspect_ctx = None):
4949
"""Check that a target is suitable for clippy and extract the `CrateInfo` provider from it.
5050
5151
Args:
@@ -72,11 +72,13 @@ def _get_clippy_ready_crate_info(target, aspect_ctx):
7272
return None
7373

7474
# Obviously ignore any targets that don't contain `CrateInfo`
75-
if rust_common.crate_info not in target:
75+
if rust_common.crate_info in target:
76+
return target[rust_common.crate_info]
77+
elif rust_common.test_crate_info in target:
78+
return target[rust_common.test_crate_info].crate
79+
else:
7680
return None
7781

78-
return target[rust_common.crate_info]
79-
8082
def _clippy_aspect_impl(target, ctx):
8183
crate_info = _get_clippy_ready_crate_info(target, ctx)
8284
if not crate_info:
@@ -225,6 +227,10 @@ rust_clippy_aspect = aspect(
225227
),
226228
},
227229
provides = [ClippyInfo],
230+
required_providers = [
231+
[rust_common.crate_info],
232+
[rust_common.test_crate_info],
233+
],
228234
toolchains = [
229235
str(Label("//rust:toolchain_type")),
230236
"@bazel_tools//tools/cpp:toolchain_type",

rust/private/rustfmt.bzl

Lines changed: 85 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,41 @@
22

33
load(":common.bzl", "rust_common")
44

5-
def _find_rustfmtable_srcs(target, aspect_ctx = None):
6-
"""Parse a target for rustfmt formattable sources.
5+
def _get_rustfmt_ready_crate_info(target):
6+
"""Check that a target is suitable for rustfmt and extract the `CrateInfo` provider from it.
77
88
Args:
99
target (Target): The target the aspect is running on.
10-
aspect_ctx (ctx, optional): The aspect's context object.
1110
1211
Returns:
13-
list: A list of formattable sources (`File`).
12+
CrateInfo, optional: A `CrateInfo` provider if clippy should be run or `None`.
1413
"""
15-
if rust_common.crate_info not in target:
16-
return []
1714

1815
# Ignore external targets
1916
if target.label.workspace_root.startswith("external"):
20-
return []
17+
return None
18+
19+
# Obviously ignore any targets that don't contain `CrateInfo`
20+
if rust_common.crate_info in target:
21+
return target[rust_common.crate_info]
22+
elif rust_common.test_crate_info in target:
23+
return target[rust_common.test_crate_info].crate
24+
else:
25+
return None
26+
27+
def _find_rustfmtable_srcs(crate_info, aspect_ctx = None):
28+
"""Parse a `CrateInfo` provider for rustfmt formattable sources.
29+
30+
Args:
31+
crate_info (CrateInfo): A `CrateInfo` provider.
32+
aspect_ctx (ctx, optional): The aspect's context object.
33+
34+
Returns:
35+
list: A list of formattable sources (`File`).
36+
"""
2137

38+
# Targets with specific tags will not be formatted
2239
if aspect_ctx:
23-
# Targets with specifc tags will not be formatted
2440
ignore_tags = [
2541
"no-format",
2642
"no-rustfmt",
@@ -31,8 +47,6 @@ def _find_rustfmtable_srcs(target, aspect_ctx = None):
3147
if tag in aspect_ctx.rule.attr.tags:
3248
return []
3349

34-
crate_info = target[rust_common.crate_info]
35-
3650
# Filter out any generated files
3751
srcs = [src for src in crate_info.srcs.to_list() if src.is_source]
3852

@@ -83,21 +97,23 @@ def _perform_check(edition, srcs, ctx):
8397
return marker
8498

8599
def _rustfmt_aspect_impl(target, ctx):
86-
srcs = _find_rustfmtable_srcs(target, ctx)
100+
crate_info = _get_rustfmt_ready_crate_info(target)
101+
102+
if not crate_info:
103+
return []
104+
105+
srcs = _find_rustfmtable_srcs(crate_info, ctx)
87106

88107
# If there are no formattable sources, do nothing.
89108
if not srcs:
90109
return []
91110

92-
# Parse the edition to use for formatting from the target
93-
edition = target[rust_common.crate_info].edition
111+
edition = crate_info.edition
94112

95-
manifest = _generate_manifest(edition, srcs, ctx)
96113
marker = _perform_check(edition, srcs, ctx)
97114

98115
return [
99116
OutputGroupInfo(
100-
rustfmt_manifest = depset([manifest]),
101117
rustfmt_checks = depset([marker]),
102118
),
103119
]
@@ -109,7 +125,6 @@ This aspect is used to gather information about a crate for use in rustfmt and p
109125
110126
Output Groups:
111127
112-
- `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
113128
- `rustfmt_checks`: Executes `rustfmt --check` on the specified target.
114129
115130
The build setting `@rules_rust//:rustfmt.toml` is used to control the Rustfmt [configuration settings][cs]
@@ -134,6 +149,48 @@ generated source files are also ignored by this aspect.
134149
default = Label("//util/process_wrapper"),
135150
),
136151
},
152+
incompatible_use_toolchain_transition = True,
153+
required_providers = [
154+
[rust_common.crate_info],
155+
[rust_common.test_crate_info],
156+
],
157+
fragments = ["cpp"],
158+
host_fragments = ["cpp"],
159+
toolchains = [
160+
str(Label("//rust/rustfmt:toolchain_type")),
161+
],
162+
)
163+
164+
def _rustfmt_test_manifest_aspect_impl(target, ctx):
165+
crate_info = _get_rustfmt_ready_crate_info(target)
166+
167+
if not crate_info:
168+
return []
169+
170+
# Parse the edition to use for formatting from the target
171+
edition = crate_info.edition
172+
173+
srcs = _find_rustfmtable_srcs(crate_info, ctx)
174+
manifest = _generate_manifest(edition, srcs, ctx)
175+
176+
return [
177+
OutputGroupInfo(
178+
rustfmt_manifest = depset([manifest]),
179+
),
180+
]
181+
182+
# This aspect contains functionality split out of `rustfmt_aspect` which broke when
183+
# `required_providers` was added to it. Aspects which have `required_providers` seems
184+
# to not function with attributes that also require providers.
185+
_rustfmt_test_manifest_aspect = aspect(
186+
implementation = _rustfmt_test_manifest_aspect_impl,
187+
doc = """\
188+
This aspect is used to gather information about a crate for use in `rustfmt_test`
189+
190+
Output Groups:
191+
192+
- `rustfmt_manifest`: A manifest used by rustfmt binaries to provide crate specific settings.
193+
""",
137194
incompatible_use_toolchain_transition = True,
138195
fragments = ["cpp"],
139196
host_fragments = ["cpp"],
@@ -158,8 +215,13 @@ def _rustfmt_test_impl(ctx):
158215
is_executable = True,
159216
)
160217

161-
manifests = depset(transitive = [target[OutputGroupInfo].rustfmt_manifest for target in ctx.attr.targets])
162-
srcs = [depset(_find_rustfmtable_srcs(target)) for target in ctx.attr.targets]
218+
crate_infos = [_get_rustfmt_ready_crate_info(target) for target in ctx.attr.targets]
219+
srcs = [depset(_find_rustfmtable_srcs(crate_info)) for crate_info in crate_infos if crate_info]
220+
221+
# Some targets may be included in tests but tagged as "no-format". In this
222+
# case, there will be no manifest.
223+
manifests = [getattr(target[OutputGroupInfo], "rustfmt_manifest", None) for target in ctx.attr.targets]
224+
manifests = depset(transitive = [manifest for manifest in manifests if manifest])
163225

164226
runfiles = ctx.runfiles(
165227
transitive_files = depset(transitive = srcs + [manifests]),
@@ -192,8 +254,11 @@ rustfmt_test = rule(
192254
attrs = {
193255
"targets": attr.label_list(
194256
doc = "Rust targets to run `rustfmt --check` on.",
195-
providers = [rust_common.crate_info],
196-
aspects = [rustfmt_aspect],
257+
providers = [
258+
[rust_common.crate_info],
259+
[rust_common.test_crate_info],
260+
],
261+
aspects = [_rustfmt_test_manifest_aspect],
197262
),
198263
"_runner": attr.label(
199264
doc = "The rustfmt test runner",

test/rustfmt/BUILD.bazel

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,9 @@
1-
load("@rules_rust//rust:defs.bzl", "rust_binary", "rustfmt_test")
1+
load(":rustfmt_integration_test_suite.bzl", "rustfmt_integration_test_suite")
22

33
exports_files([
44
"test_rustfmt.toml",
55
])
66

7-
rust_binary(
8-
name = "formatted_2018",
9-
srcs = ["srcs/2018/formatted.rs"],
10-
edition = "2018",
11-
)
12-
13-
rustfmt_test(
14-
name = "test_formatted_2018",
15-
targets = [":formatted_2018"],
16-
)
17-
18-
rust_binary(
19-
name = "unformatted_2018",
20-
srcs = ["srcs/2018/unformatted.rs"],
21-
edition = "2018",
22-
tags = ["norustfmt"],
23-
)
24-
25-
rustfmt_test(
26-
name = "test_unformatted_2018",
27-
tags = ["manual"],
28-
targets = [":unformatted_2018"],
29-
)
30-
31-
rust_binary(
32-
name = "formatted_2015",
33-
srcs = ["srcs/2015/formatted.rs"],
34-
edition = "2015",
35-
)
36-
37-
rustfmt_test(
38-
name = "test_formatted_2015",
39-
targets = [":formatted_2015"],
40-
)
41-
42-
rust_binary(
43-
name = "unformatted_2015",
44-
srcs = ["srcs/2015/unformatted.rs"],
45-
edition = "2015",
46-
tags = ["norustfmt"],
47-
)
48-
49-
rustfmt_test(
50-
name = "test_unformatted_2015",
51-
tags = ["manual"],
52-
targets = [":unformatted_2015"],
53-
)
54-
55-
sh_binary(
56-
name = "test_runner",
57-
srcs = ["rustfmt_failure_test.sh"],
7+
rustfmt_integration_test_suite(
8+
name = "rustfmt_integration_test_suite",
589
)

test/rustfmt/rustfmt_failure_test.sh

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ function check_build_result() {
3636
function test_all_and_apply() {
3737
local -r TEST_OK=0
3838
local -r TEST_FAILED=3
39+
local -r VARIANTS=(rust_binary rust_library rust_shared_library rust_static_library)
3940

4041
temp_dir="$(mktemp -d -t ci-XXXXXXXXXX)"
4142
new_workspace="${temp_dir}/rules_rust_test_rustfmt"
@@ -58,30 +59,35 @@ EOF
5859
else
5960
SEDOPTS=(-i)
6061
fi
61-
sed ${SEDOPTS[@]} 's/"norustfmt"//' "${new_workspace}/test/rustfmt/BUILD.bazel"
62+
sed ${SEDOPTS[@]} 's/"norustfmt"//' "${new_workspace}/test/rustfmt/rustfmt_integration_test_suite.bzl"
63+
sed ${SEDOPTS[@]} 's/"manual"//' "${new_workspace}/test/rustfmt/rustfmt_integration_test_suite.bzl"
6264

6365
pushd "${new_workspace}"
6466

65-
check_build_result $TEST_FAILED test_unformatted_2015
66-
check_build_result $TEST_FAILED test_unformatted_2018
67-
check_build_result $TEST_OK test_formatted_2015
68-
check_build_result $TEST_OK test_formatted_2018
67+
for variant in ${VARIANTS[@]}; do
68+
check_build_result $TEST_FAILED ${variant}_unformatted_2015_test
69+
check_build_result $TEST_FAILED ${variant}_unformatted_2018_test
70+
check_build_result $TEST_OK ${variant}_formatted_2015_test
71+
check_build_result $TEST_OK ${variant}_formatted_2018_test
72+
done
6973

7074
# Format a specific target
71-
bazel run @rules_rust//tools/rustfmt -- //test/rustfmt:unformatted_2018
75+
for variant in ${VARIANTS[@]}; do
76+
bazel run @rules_rust//tools/rustfmt -- //test/rustfmt:${variant}_unformatted_2018
77+
done
7278

73-
check_build_result $TEST_FAILED test_unformatted_2015
74-
check_build_result $TEST_OK test_unformatted_2018
75-
check_build_result $TEST_OK test_formatted_2015
76-
check_build_result $TEST_OK test_formatted_2018
79+
for variant in ${VARIANTS[@]}; do
80+
check_build_result $TEST_FAILED ${variant}_unformatted_2015_test
81+
check_build_result $TEST_OK ${variant}_unformatted_2018_test
82+
check_build_result $TEST_OK ${variant}_formatted_2015_test
83+
check_build_result $TEST_OK ${variant}_formatted_2018_test
84+
done
7785

7886
# Format all targets
7987
bazel run @rules_rust//tools/rustfmt --@rules_rust//:rustfmt.toml=//test/rustfmt:test_rustfmt.toml
8088

81-
check_build_result $TEST_OK test_unformatted_2015
82-
check_build_result $TEST_OK test_unformatted_2018
83-
check_build_result $TEST_OK test_formatted_2015
84-
check_build_result $TEST_OK test_formatted_2018
89+
# Ensure all tests pass
90+
check_build_result $TEST_OK "*"
8591

8692
popd
8793

0 commit comments

Comments
 (0)