From 828c8071f39aa94a3bcfebb391b864aeb41453e9 Mon Sep 17 00:00:00 2001 From: UebelAndre Date: Wed, 27 Dec 2023 09:02:43 -0800 Subject: [PATCH] Added `rust_unpretty_aspect` and `rust_unpretty` rules (#2356) Proposal for https://github.com/bazelbuild/rules_rust/issues/1642 Duplicates https://github.com/bazelbuild/rules_rust/pull/1643 (special thanks to @freeformstu) ### Summary Rustc can be used to expand all macros so that you can inspect the generated source files easier. This feature is enabled via `-Zunpretty={mode}`. The `-Z` flag is only available in the nightly version of `rustc` (https://github.com/rust-lang/rust/issues/43364). ### Unprettying Build and test your targets normally. ``` bazel build //:ok_binary INFO: Analyzed target //:ok_binary (0 packages loaded, 0 targets configured). INFO: Found 1 target... Target //:ok_binary up-to-date: bazel-bin/ok_binary INFO: Elapsed time: 0.081s, Critical Path: 0.00s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action ``` Use the aspect to generate the expanded files in as a one-off build. (`.bazelrc`) ``` # Enable unpretty for all targets in the workspace build:unpretty --aspects=@rules_rust//rust:defs.bzl%rust_unpretty_aspect build:unpretty --output_groups=+rust_unpretty # `unpretty` requires the nightly toolchain. See tracking issue: # https://github.com/rust-lang/rust/issues/43364 build:unpretty --@rules_rust//rust/toolchain/channel=nightly ``` ``` bazel build --config=unpretty //:ok_binary INFO: Analyzed target //:ok_binary (1 packages loaded, 2 targets configured). INFO: Found 1 target... Aspect @rules_rust//rust/private:unpretty.bzl%rust_unpretty_aspect of //:ok_binary up-to-date: bazel-bin/ok_binary.expand.rs INFO: Elapsed time: 0.149s, Critical Path: 0.00s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action ``` Targeting tests is valid as well. ``` bazel build --config=unpretty //:ok_test INFO: Analyzed target //:ok_test (0 packages loaded, 2 targets configured). INFO: Found 1 target... Aspect @rules_rust//rust/private:unpretty.bzl%rust_expand_aspect of //:ok_test up-to-date: bazel-bin/test-397521499/ok_test.expand.rs INFO: Elapsed time: 0.113s, Critical Path: 0.00s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action ``` Finally, manually wire up a `rust_unpretty` target explicitly if you want a target to build. This rule is unique compared to the aspect in that it forces a transition to a nightly toolchain so that `-Zunpretty` can be used. ```starlark load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_unpretty") rust_binary( name = "ok_binary", srcs = ["src/main.rs"], edition = "2021", ) rust_unpretty( name = "ok_binary_expand", deps = [":ok_binary"], ) ``` ``` bazel build //:ok_binary_expand INFO: Analyzed target //:ok_binary_expand (0 packages loaded, 1 target configured). INFO: Found 1 target... Target //:ok_binary_expand up-to-date: bazel-bin/ok_binary.expand.rs INFO: Elapsed time: 0.090s, Critical Path: 0.00s INFO: 1 process: 1 internal. INFO: Build completed successfully, 1 total action ``` --- .bazelci/presubmit.yml | 8 + .bazelrc | 8 + rust/defs.bzl | 11 + rust/private/rust.bzl | 120 +++--- rust/private/unpretty.bzl | 344 ++++++++++++++++++ rust/settings/BUILD.bazel | 20 + .../force_all_deps_direct_test.bzl | 5 +- test/unpretty/BUILD.bazel | 49 +++ test/unpretty/proc_macro.rs | 9 + test/unpretty/proc_macro.unpretty.expanded.rs | 24 ++ test/unpretty/proc_macro_test.rs | 8 + .../proc_macro_test.unpretty.expanded.rs | 9 + 12 files changed, 565 insertions(+), 50 deletions(-) create mode 100644 rust/private/unpretty.bzl create mode 100644 test/unpretty/BUILD.bazel create mode 100644 test/unpretty/proc_macro.rs create mode 100644 test/unpretty/proc_macro.unpretty.expanded.rs create mode 100644 test/unpretty/proc_macro_test.rs create mode 100644 test/unpretty/proc_macro_test.unpretty.expanded.rs diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index 9d23e7b247..80f88d4bbc 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -15,6 +15,7 @@ single_rust_channel_targets: &single_rust_channel_targets - "//..." # These tests are expected to fail as they require both a nightly and stable toolchain. - "-//test/unit/channel_transitions/..." + - "-//test/unpretty/..." default_linux_targets: &default_linux_targets - "--" - "//..." @@ -169,6 +170,13 @@ tasks: test_targets: *default_windows_targets soft_fail: yes bazel: "rolling" + ubuntu2004_unpretty: + name: Unpretty + platform: ubuntu2004 + build_targets: *default_linux_targets + test_targets: *default_linux_targets + build_flags: + - "--config=unpretty" ubuntu2004_bzlmod_only: name: With bzlmod platform: ubuntu2004 diff --git a/.bazelrc b/.bazelrc index 00e698c685..0667486fb5 100644 --- a/.bazelrc +++ b/.bazelrc @@ -32,6 +32,14 @@ build:rustfmt --output_groups=+rustfmt_checks build:clippy --aspects=//rust:defs.bzl%rust_clippy_aspect build:clippy --output_groups=+clippy_checks +# Enable unpretty for all targets in the workspace +build:unpretty --aspects=//rust:defs.bzl%rust_unpretty_aspect +build:unpretty --output_groups=+rust_unpretty + +# `unpretty` requires the nightly toolchain. See tracking issue: +# https://github.com/rust-lang/rust/issues/43364 +build:unpretty --//rust/toolchain/channel=nightly + ############################################################################### ## Incompatibility flags ############################################################################### diff --git a/rust/defs.bzl b/rust/defs.bzl index 8573048e3d..3cee1afe54 100644 --- a/rust/defs.bzl +++ b/rust/defs.bzl @@ -65,6 +65,11 @@ load( _rustfmt_aspect = "rustfmt_aspect", _rustfmt_test = "rustfmt_test", ) +load( + "//rust/private:unpretty.bzl", + _rust_unpretty = "rust_unpretty", + _rust_unpretty_aspect = "rust_unpretty_aspect", +) rust_library = _rust_library # See @rules_rust//rust/private:rust.bzl for a complete description. @@ -111,6 +116,12 @@ capture_clippy_output = _capture_clippy_output rustc_output_diagnostics = _rustc_output_diagnostics # See @rules_rust//rust/private:rustc.bzl for a complete description. +rust_unpretty_aspect = _rust_unpretty_aspect +# See @rules_rust//rust/private:unpretty.bzl for a complete description. + +rust_unpretty = _rust_unpretty +# See @rules_rust//rust/private:unpretty.bzl for a complete description. + error_format = _error_format # See @rules_rust//rust/private:rustc.bzl for a complete description. diff --git a/rust/private/rust.bzl b/rust/private/rust.bzl index 3e89d50ae9..bed6f81c46 100644 --- a/rust/private/rust.bzl +++ b/rust/private/rust.bzl @@ -266,6 +266,22 @@ def _rust_binary_impl(ctx): return providers +def get_rust_test_flags(attr): + """Determine the desired rustc flags for test targets. + + Args: + attr (dict): Attributes of a rule + + Returns: + List: A list of test flags + """ + if getattr(attr, "use_libtest_harness", True): + rust_flags = ["--test"] + else: + rust_flags = ["--cfg", "test"] + + return rust_flags + def _rust_test_impl(ctx): """The implementation of the `rust_test` rule. @@ -394,7 +410,7 @@ def _rust_test_impl(ctx): attr = ctx.attr, toolchain = toolchain, crate_info_dict = crate_info_dict, - rust_flags = ["--test"] if ctx.attr.use_libtest_harness else ["--cfg", "test"], + rust_flags = get_rust_test_flags(ctx.attr), skip_expanding_rustc_env = True, ) data = getattr(ctx.attr, "data", []) @@ -487,6 +503,56 @@ def _stamp_attribute(default_value): values = [1, 0, -1], ) +# Internal attributes core to Rustc actions. +RUSTC_ATTRS = { + "_cc_toolchain": attr.label( + doc = ( + "In order to use find_cc_toolchain, your rule has to depend " + + "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " + + "docs for details." + ), + default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), + ), + "_error_format": attr.label( + default = Label("//:error_format"), + ), + "_extra_exec_rustc_flag": attr.label( + default = Label("//:extra_exec_rustc_flag"), + ), + "_extra_exec_rustc_flags": attr.label( + default = Label("//:extra_exec_rustc_flags"), + ), + "_extra_rustc_flag": attr.label( + default = Label("//:extra_rustc_flag"), + ), + "_extra_rustc_flags": attr.label( + default = Label("//:extra_rustc_flags"), + ), + "_import_macro_dep": attr.label( + default = Label("//util/import"), + cfg = "exec", + ), + "_is_proc_macro_dep": attr.label( + default = Label("//rust/private:is_proc_macro_dep"), + ), + "_is_proc_macro_dep_enabled": attr.label( + default = Label("//rust/private:is_proc_macro_dep_enabled"), + ), + "_per_crate_rustc_flag": attr.label( + default = Label("//:experimental_per_crate_rustc_flag"), + ), + "_process_wrapper": attr.label( + doc = "A process wrapper for running rustc on all platforms.", + default = Label("//util/process_wrapper"), + executable = True, + allow_single_file = True, + cfg = "exec", + ), + "_rustc_output_diagnostics": attr.label( + default = Label("//:rustc_output_diagnostics"), + ), +} + _common_attrs = { "aliases": attr.label_keyed_string_dict( doc = dedent("""\ @@ -618,62 +684,18 @@ _common_attrs = { """), allow_files = [".rs"], ), - "stamp": _stamp_attribute(default_value = 0), + "stamp": _stamp_attribute( + default_value = 0, + ), "version": attr.string( doc = "A version to inject in the cargo environment variable.", default = "0.0.0", ), - "_cc_toolchain": attr.label( - doc = ( - "In order to use find_cc_toolchain, your rule has to depend " + - "on C++ toolchain. See `@rules_cc//cc:find_cc_toolchain.bzl` " + - "docs for details." - ), - default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"), - ), - "_error_format": attr.label( - default = Label("//:error_format"), - ), - "_extra_exec_rustc_flag": attr.label( - default = Label("//:extra_exec_rustc_flag"), - ), - "_extra_exec_rustc_flags": attr.label( - default = Label("//:extra_exec_rustc_flags"), - ), - "_extra_rustc_flag": attr.label( - default = Label("//:extra_rustc_flag"), - ), - "_extra_rustc_flags": attr.label( - default = Label("//:extra_rustc_flags"), - ), - "_import_macro_dep": attr.label( - default = Label("//util/import"), - cfg = "exec", - ), - "_is_proc_macro_dep": attr.label( - default = Label("//rust/private:is_proc_macro_dep"), - ), - "_is_proc_macro_dep_enabled": attr.label( - default = Label("//rust/private:is_proc_macro_dep_enabled"), - ), - "_per_crate_rustc_flag": attr.label( - default = Label("//:experimental_per_crate_rustc_flag"), - ), - "_process_wrapper": attr.label( - doc = "A process wrapper for running rustc on all platforms.", - default = Label("//util/process_wrapper"), - executable = True, - allow_single_file = True, - cfg = "exec", - ), - "_rustc_output_diagnostics": attr.label( - default = Label("//:rustc_output_diagnostics"), - ), "_stamp_flag": attr.label( doc = "A setting used to determine whether or not the `--stamp` flag is enabled", default = Label("//rust/private:stamp"), ), -} +} | RUSTC_ATTRS _coverage_attrs = { "_collect_cc_coverage": attr.label( diff --git a/rust/private/unpretty.bzl b/rust/private/unpretty.bzl new file mode 100644 index 0000000000..81b04e0f5b --- /dev/null +++ b/rust/private/unpretty.bzl @@ -0,0 +1,344 @@ +"""A module defining Rust 'unpretty' rules""" + +load("//rust/private:common.bzl", "rust_common") +load( + "//rust/private:rust.bzl", + "RUSTC_ATTRS", + "get_rust_test_flags", +) +load( + "//rust/private:rustc.bzl", + "collect_deps", + "collect_inputs", + "construct_arguments", +) +load( + "//rust/private:utils.bzl", + "determine_output_hash", + "find_cc_toolchain", + "find_toolchain", +) + +# This list is determined by running the following command: +# +# rustc +nightly -Zunpretty= +# +_UNPRETTY_MODES = [ + "ast-tree,expanded", + "ast-tree", + "expanded,hygiene", + "expanded,identified", + "expanded", + "hir-tree", + "hir,identified", + "hir,typed", + "hir", + "identified", + "mir-cfg", + "mir", + "normal", +] + +RustUnprettyInfo = provider( + doc = "A provider describing the Rust unpretty mode.", + fields = { + "modes": "Depset[string]: Can be any of {}".format(["'{}'".format(m) for m in _UNPRETTY_MODES]), + }, +) + +def _rust_unpretty_flag_impl(ctx): + value = ctx.build_setting_value + invalid = [] + for mode in value: + if mode not in _UNPRETTY_MODES: + invalid.append(mode) + if invalid: + fail("{} build setting allowed to take values [{}] but was set to unallowed values: {}".format( + ctx.label, + ", ".join(["'{}'".format(m) for m in _UNPRETTY_MODES]), + invalid, + )) + + return RustUnprettyInfo(modes = depset(value)) + +rust_unpretty_flag = rule( + doc = "A build setting which represents the Rust unpretty mode. The allowed values are {}".format(_UNPRETTY_MODES), + implementation = _rust_unpretty_flag_impl, + build_setting = config.string_list( + flag = True, + repeatable = True, + ), +) + +def _nightly_unpretty_transition_impl(settings, attr): + mode = settings[str(Label("//rust/settings:unpretty"))] + + # Use the presence of _unpretty_modes as a proxy for whether this is a rust_unpretty target. + if hasattr(attr, "_unpretty_modes") and hasattr(attr, "mode"): + mode = mode + [attr.mode] + + return { + str(Label("//rust/settings:unpretty")): depset(mode).to_list(), + str(Label("//rust/toolchain/channel")): "nightly", + } + +nightly_unpretty_transition = transition( + implementation = _nightly_unpretty_transition_impl, + inputs = [str(Label("//rust/settings:unpretty"))], + outputs = [ + str(Label("//rust/settings:unpretty")), + str(Label("//rust/toolchain/channel")), + ], +) + +def _get_unpretty_ready_crate_info(target, aspect_ctx): + """Check that a target is suitable for expansion and extract the `CrateInfo` provider from it. + + Args: + target (Target): The target the aspect is running on. + aspect_ctx (ctx, optional): The aspect's context object. + + Returns: + CrateInfo, optional: A `CrateInfo` provider if rust unpretty should be run or `None`. + """ + + # Ignore external targets + if target.label.workspace_root.startswith("external"): + return None + + # Targets with specific tags will not be formatted + if aspect_ctx: + ignore_tags = [ + "nounpretty", + "no-unpretty", + "no_unpretty", + ] + + for tag in ignore_tags: + if tag in aspect_ctx.rule.attr.tags: + return None + + # Obviously ignore any targets that don't contain `CrateInfo` + if rust_common.crate_info not in target: + return None + + return target[rust_common.crate_info] + +def _rust_unpretty_aspect_impl(target, ctx): + crate_info = _get_unpretty_ready_crate_info(target, ctx) + if not crate_info: + return [] + + toolchain = find_toolchain(ctx) + cc_toolchain, feature_configuration = find_cc_toolchain(ctx) + + dep_info, build_info, linkstamps = collect_deps( + deps = crate_info.deps, + proc_macro_deps = crate_info.proc_macro_deps, + aliases = crate_info.aliases, + # Rust expand doesn't need to invoke transitive linking, therefore doesn't need linkstamps. + are_linkstamps_supported = False, + ) + + compile_inputs, out_dir, build_env_files, build_flags_files, linkstamp_outs, ambiguous_libs = collect_inputs( + ctx, + ctx.rule.file, + ctx.rule.files, + linkstamps, + toolchain, + cc_toolchain, + feature_configuration, + crate_info, + dep_info, + build_info, + ) + + output_groups = {} + outputs = [] + + for mode in ctx.attr._unpretty_modes[RustUnprettyInfo].modes.to_list(): + pretty_mode = mode.replace("-", "_") + mnemonic = "RustUnpretty{}".format("".join([ + o.title() + for m in pretty_mode.split(",") + for o in m.split("_") + ])) + + unpretty_out = ctx.actions.declare_file( + "{}.unpretty.{}.rs".format(ctx.label.name, pretty_mode.replace(",", ".")), + sibling = crate_info.output, + ) + + output_groups.update({"rust_unpretty_{}".format(pretty_mode.replace(",", "_")): depset([unpretty_out])}) + outputs.append(unpretty_out) + + rust_flags = [] + if crate_info.is_test: + rust_flags = get_rust_test_flags(ctx.rule.attr) + + args, env = construct_arguments( + ctx = ctx, + attr = ctx.rule.attr, + file = ctx.file, + toolchain = toolchain, + tool_path = toolchain.rustc.path, + cc_toolchain = cc_toolchain, + feature_configuration = feature_configuration, + crate_info = crate_info, + dep_info = dep_info, + linkstamp_outs = linkstamp_outs, + ambiguous_libs = ambiguous_libs, + output_hash = determine_output_hash(crate_info.root, ctx.label), + rust_flags = rust_flags, + out_dir = out_dir, + build_env_files = build_env_files, + build_flags_files = build_flags_files, + emit = ["dep-info", "metadata"], + skip_expanding_rustc_env = True, + ) + + args.process_wrapper_flags.add("--stdout-file", unpretty_out) + + # Expand all macros and dump the source to stdout. + # Tracking issue: https://github.com/rust-lang/rust/issues/43364 + args.rustc_flags.add("-Zunpretty={}".format(mode)) + + ctx.actions.run( + executable = ctx.executable._process_wrapper, + inputs = compile_inputs, + outputs = [unpretty_out], + env = env, + arguments = args.all, + mnemonic = mnemonic, + toolchain = "@rules_rust//rust:toolchain_type", + ) + + output_groups.update({"rust_unpretty": depset(outputs)}) + + return [ + OutputGroupInfo(**output_groups), + ] + +# Example: Expand all rust targets in the codebase. +# bazel build --aspects=@rules_rust//rust:defs.bzl%rust_unpretty_aspect \ +# --output_groups=expanded \ +# //... +rust_unpretty_aspect = aspect( + implementation = _rust_unpretty_aspect_impl, + fragments = ["cpp"], + host_fragments = ["cpp"], + attrs = { + "_unpretty_modes": attr.label( + doc = "The values to pass to `--unpretty`", + providers = [RustUnprettyInfo], + default = Label("//rust/settings:unpretty"), + ), + } | RUSTC_ATTRS, + toolchains = [ + str(Label("//rust:toolchain_type")), + "@bazel_tools//tools/cpp:toolchain_type", + ], + required_providers = [rust_common.crate_info], + doc = """\ +Executes Rust expand on specified targets. + +This aspect applies to existing rust_library, rust_test, and rust_binary rules. + +As an example, if the following is defined in `examples/hello_lib/BUILD.bazel`: + +```python +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_test( + name = "greeting_test", + srcs = ["tests/greeting.rs"], + deps = [":hello_lib"], +) +``` + +Then the targets can be expanded with the following command: + +```output +$ bazel build --aspects=@rules_rust//rust:defs.bzl%rust_unpretty_aspect \ + --output_groups=rust_unpretty_expanded //hello_lib:all +``` +""", +) + +def _rust_unpretty_rule_impl(ctx): + mode = ctx.attr.mode + output_group = "rust_unpretty_{}".format(mode.replace(",", "_").replace("-", "_")) + files = [] + for target in ctx.attr.deps: + files.append(getattr(target[OutputGroupInfo], output_group)) + + return [DefaultInfo(files = depset(transitive = files))] + +rust_unpretty = rule( + implementation = _rust_unpretty_rule_impl, + cfg = nightly_unpretty_transition, + attrs = { + "deps": attr.label_list( + doc = "Rust targets to run unpretty on.", + providers = [rust_common.crate_info], + aspects = [rust_unpretty_aspect], + ), + "mode": attr.string( + doc = "The value to pass to `--unpretty`", + values = _UNPRETTY_MODES, + default = "expanded", + ), + "_allowlist_function_transition": attr.label( + default = Label("//tools/allowlists/function_transition_allowlist"), + ), + "_unpretty_modes": attr.label( + doc = "The values to pass to `--unpretty`", + providers = [RustUnprettyInfo], + default = Label("//rust/settings:unpretty"), + ), + }, + doc = """\ +Executes rust unpretty on a specific target. + +Similar to `rust_unpretty_aspect`, but allows specifying a list of dependencies \ +within the build system. + +For example, given the following example targets: + +```python +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +rust_library( + name = "hello_lib", + srcs = ["src/lib.rs"], +) + +rust_test( + name = "greeting_test", + srcs = ["tests/greeting.rs"], + deps = [":hello_lib"], +) +``` + +Rust expand can be set as a build target with the following: + +```python +load("@rules_rust//rust:defs.bzl", "rust_unpretty") + +rust_unpretty( + name = "hello_library_expand", + testonly = True, + deps = [ + ":hello_lib", + ":greeting_test", + ], + mode = "expand", +) +``` +""", +) diff --git a/rust/settings/BUILD.bazel b/rust/settings/BUILD.bazel index f1ba202552..1f23709fb7 100644 --- a/rust/settings/BUILD.bazel +++ b/rust/settings/BUILD.bazel @@ -1,5 +1,6 @@ load("@bazel_skylib//:bzl_library.bzl", "bzl_library") load("@bazel_skylib//rules:common_settings.bzl", "bool_flag", "string_flag") +load("//rust/private:unpretty.bzl", "rust_unpretty_flag") load(":incompatible.bzl", "incompatible_flag") package(default_visibility = ["//visibility:public"]) @@ -9,6 +10,25 @@ bzl_library( srcs = glob(["**/*.bzl"]), ) +rust_unpretty_flag( + name = "unpretty", + build_setting_default = [ + "ast-tree,expanded", + "ast-tree", + "expanded,hygiene", + "expanded,identified", + "expanded", + "hir-tree", + "hir,identified", + "hir,typed", + "hir", + "identified", + "mir-cfg", + "mir", + "normal", + ], +) + # A flag controlling whether to rename first-party crates such that their names # encode the Bazel package and target name, instead of just the target name. # diff --git a/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl b/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl index 6cb5147c1a..39e705aaf0 100644 --- a/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl +++ b/test/unit/force_all_deps_direct/force_all_deps_direct_test.bzl @@ -36,7 +36,10 @@ def _force_all_deps_direct_test(): generator( name = "generate", deps = [":direct"], - tags = ["noclippy"], + tags = [ + "no-clippy", + "no-unpretty", + ], ) force_all_deps_direct_test( diff --git a/test/unpretty/BUILD.bazel b/test/unpretty/BUILD.bazel new file mode 100644 index 0000000000..3f0f77363f --- /dev/null +++ b/test/unpretty/BUILD.bazel @@ -0,0 +1,49 @@ +load("@bazel_skylib//rules:diff_test.bzl", "diff_test") +load("//rust:defs.bzl", "rust_proc_macro", "rust_test", "rust_unpretty") + +rust_proc_macro( + name = "proc_macro", + srcs = ["proc_macro.rs"], + edition = "2021", + visibility = ["//test:__subpackages__"], +) + +rust_unpretty( + name = "proc_macro_unpretty", + testonly = True, + mode = "expanded", + deps = [":proc_macro"], +) + +diff_test( + name = "proc_macro_unpretty_diff_test", + file1 = "proc_macro.unpretty.expanded.rs", + file2 = ":proc_macro_unpretty", +) + +rust_test( + name = "proc_macro_test", + srcs = ["proc_macro_test.rs"], + edition = "2021", + proc_macro_deps = [":proc_macro"], +) + +rust_unpretty( + name = "proc_macro_test_unpretty", + testonly = True, + mode = "expanded", + deps = [":proc_macro_test"], +) + +rust_unpretty( + name = "proc_macro_test_unpretty_extra", + testonly = True, + mode = "normal", + deps = [":proc_macro_test"], +) + +diff_test( + name = "proc_macro_test_unpretty_diff_test", + file1 = "proc_macro.unpretty.expanded.rs", + file2 = ":proc_macro_unpretty", +) diff --git a/test/unpretty/proc_macro.rs b/test/unpretty/proc_macro.rs new file mode 100644 index 0000000000..9005e0d32d --- /dev/null +++ b/test/unpretty/proc_macro.rs @@ -0,0 +1,9 @@ +// This differs from the edition 2015 version because it does not have an `extern proc_macro` +// statement, which became optional in edition 2018. + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn make_answer(_item: TokenStream) -> TokenStream { + "fn answer() -> u32 { 42 }".parse().unwrap() +} diff --git a/test/unpretty/proc_macro.unpretty.expanded.rs b/test/unpretty/proc_macro.unpretty.expanded.rs new file mode 100644 index 0000000000..f167085f2a --- /dev/null +++ b/test/unpretty/proc_macro.unpretty.expanded.rs @@ -0,0 +1,24 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +// This differs from the edition 2015 version because it does not have an `extern proc_macro` +// statement, which became optional in edition 2018. + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn make_answer(_item: TokenStream) -> TokenStream { + "fn answer() -> u32 { 42 }".parse().unwrap() +} +const _: () = + { + extern crate proc_macro; + #[rustc_proc_macro_decls] + #[used] + #[allow(deprecated)] + static _DECLS: &[proc_macro::bridge::client::ProcMacro] = + &[proc_macro::bridge::client::ProcMacro::bang("make_answer", + make_answer)]; + }; diff --git a/test/unpretty/proc_macro_test.rs b/test/unpretty/proc_macro_test.rs new file mode 100644 index 0000000000..c5f92f044f --- /dev/null +++ b/test/unpretty/proc_macro_test.rs @@ -0,0 +1,8 @@ +use proc_macro::make_answer; + +make_answer!(); + +#[test] +fn test_answer_macro() { + println!("{}", answer()); +} diff --git a/test/unpretty/proc_macro_test.unpretty.expanded.rs b/test/unpretty/proc_macro_test.unpretty.expanded.rs new file mode 100644 index 0000000000..7261e999e9 --- /dev/null +++ b/test/unpretty/proc_macro_test.unpretty.expanded.rs @@ -0,0 +1,9 @@ +#![feature(prelude_import)] +#[prelude_import] +use std::prelude::rust_2021::*; +#[macro_use] +extern crate std; +use proc_macro::make_answer; + +fn answer() -> u32 { 42 } +