Squashed 'third_party/rules_rust/' content from commit bf59038ca

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/rust/private/clippy.bzl b/rust/private/clippy.bzl
new file mode 100644
index 0000000..d1464e9
--- /dev/null
+++ b/rust/private/clippy.bzl
@@ -0,0 +1,304 @@
+# Copyright 2020 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""A module defining clippy rules"""
+
+load("//rust/private:common.bzl", "rust_common")
+load("//rust/private:providers.bzl", "CaptureClippyOutputInfo", "ClippyInfo")
+load(
+    "//rust/private:rustc.bzl",
+    "collect_deps",
+    "collect_inputs",
+    "construct_arguments",
+)
+load(
+    "//rust/private:utils.bzl",
+    "determine_output_hash",
+    "find_cc_toolchain",
+    "find_toolchain",
+)
+
+def _get_clippy_ready_crate_info(target, aspect_ctx):
+    """Check that a target is suitable for clippy 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 clippy should be run or `None`.
+    """
+
+    # Ignore external targets
+    if target.label.workspace_root.startswith("external"):
+        return None
+
+    # Targets annotated with `noclippy` will not be formatted
+    if aspect_ctx and "noclippy" 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 _clippy_aspect_impl(target, ctx):
+    crate_info = _get_clippy_ready_crate_info(target, ctx)
+    if not crate_info:
+        return [ClippyInfo(output = depset([]))]
+
+    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,
+        # Clippy 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,
+    )
+
+    args, env = construct_arguments(
+        ctx = ctx,
+        attr = ctx.rule.attr,
+        file = ctx.file,
+        toolchain = toolchain,
+        tool_path = toolchain.clippy_driver.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 = [],
+        out_dir = out_dir,
+        build_env_files = build_env_files,
+        build_flags_files = build_flags_files,
+        emit = ["dep-info", "metadata"],
+    )
+
+    if crate_info.is_test:
+        args.rustc_flags.add("--test")
+
+    # For remote execution purposes, the clippy_out file must be a sibling of crate_info.output
+    # or rustc may fail to create intermediate output files because the directory does not exist.
+    if ctx.attr._capture_output[CaptureClippyOutputInfo].capture_output:
+        clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.out", sibling = crate_info.output)
+        args.process_wrapper_flags.add("--stderr-file", clippy_out.path)
+
+        # If we are capturing the output, we want the build system to be able to keep going
+        # and consume the output. Some clippy lints are denials, so we treat them as warnings.
+        args.rustc_flags.add("-Wclippy::all")
+    else:
+        # A marker file indicating clippy has executed successfully.
+        # This file is necessary because "ctx.actions.run" mandates an output.
+        clippy_out = ctx.actions.declare_file(ctx.label.name + ".clippy.ok", sibling = crate_info.output)
+        args.process_wrapper_flags.add("--touch-file", clippy_out.path)
+
+        # Turn any warnings from clippy or rustc into an error, as otherwise
+        # Bazel will consider the execution result of the aspect to be "success",
+        # and Clippy won't be re-triggered unless the source file is modified.
+        if "__bindgen" in ctx.rule.attr.tags:
+            # bindgen-generated content is likely to trigger warnings, so
+            # only fail on clippy warnings
+            args.rustc_flags.add("-Dclippy::style")
+            args.rustc_flags.add("-Dclippy::correctness")
+            args.rustc_flags.add("-Dclippy::complexity")
+            args.rustc_flags.add("-Dclippy::perf")
+        else:
+            # fail on any warning
+            args.rustc_flags.add("-Dwarnings")
+
+    # Upstream clippy requires one of these two filenames or it silently uses
+    # the default config. Enforce the naming so users are not confused.
+    valid_config_file_names = [".clippy.toml", "clippy.toml"]
+    if ctx.file._config.basename not in valid_config_file_names:
+        fail("The clippy config file must be named one of: {}".format(valid_config_file_names))
+    env["CLIPPY_CONF_DIR"] = "${{pwd}}/{}".format(ctx.file._config.dirname)
+    compile_inputs = depset([ctx.file._config], transitive = [compile_inputs])
+
+    ctx.actions.run(
+        executable = ctx.executable._process_wrapper,
+        inputs = compile_inputs,
+        outputs = [clippy_out],
+        env = env,
+        tools = [toolchain.clippy_driver],
+        arguments = args.all,
+        mnemonic = "Clippy",
+    )
+
+    return [
+        OutputGroupInfo(clippy_checks = depset([clippy_out])),
+        ClippyInfo(output = depset([clippy_out])),
+    ]
+
+# Example: Run the clippy checker on all targets in the codebase.
+#   bazel build --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect \
+#               --output_groups=clippy_checks \
+#               //...
+rust_clippy_aspect = aspect(
+    fragments = ["cpp"],
+    host_fragments = ["cpp"],
+    attrs = {
+        "_capture_output": attr.label(
+            doc = "Value of the `capture_clippy_output` build setting",
+            default = Label("//:capture_clippy_output"),
+        ),
+        "_cc_toolchain": attr.label(
+            doc = (
+                "Required attribute to access the cc_toolchain. See [Accessing the C++ toolchain]" +
+                "(https://docs.bazel.build/versions/master/integrating-with-rules-cc.html#accessing-the-c-toolchain)"
+            ),
+            default = Label("@bazel_tools//tools/cpp:current_cc_toolchain"),
+        ),
+        "_config": attr.label(
+            doc = "The `clippy.toml` file used for configuration",
+            allow_single_file = True,
+            default = Label("//:clippy.toml"),
+        ),
+        "_error_format": attr.label(
+            doc = "The desired `--error-format` flags for clippy",
+            default = "//:error_format",
+        ),
+        "_extra_rustc_flags": attr.label(default = "//:extra_rustc_flags"),
+        "_process_wrapper": attr.label(
+            doc = "A process wrapper for running clippy on all platforms",
+            default = Label("//util/process_wrapper"),
+            executable = True,
+            cfg = "exec",
+        ),
+    },
+    provides = [ClippyInfo],
+    toolchains = [
+        str(Label("//rust:toolchain")),
+        "@bazel_tools//tools/cpp:toolchain_type",
+    ],
+    incompatible_use_toolchain_transition = True,
+    implementation = _clippy_aspect_impl,
+    doc = """\
+Executes the clippy checker 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 analyzed with clippy using the following command:
+
+```output
+$ bazel build --aspects=@rules_rust//rust:defs.bzl%rust_clippy_aspect \
+              --output_groups=clippy_checks //hello_lib:all
+```
+""",
+)
+
+def _rust_clippy_rule_impl(ctx):
+    clippy_ready_targets = [dep for dep in ctx.attr.deps if "clippy_checks" in dir(dep[OutputGroupInfo])]
+    files = depset([], transitive = [dep[OutputGroupInfo].clippy_checks for dep in clippy_ready_targets])
+    return [DefaultInfo(files = files)]
+
+rust_clippy = rule(
+    implementation = _rust_clippy_rule_impl,
+    attrs = {
+        "deps": attr.label_list(
+            doc = "Rust targets to run clippy on.",
+            providers = [rust_common.crate_info],
+            aspects = [rust_clippy_aspect],
+        ),
+    },
+    doc = """\
+Executes the clippy checker on a specific target.
+
+Similar to `rust_clippy_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 clippy can be set as a build target with the following:
+
+```python
+load("@rules_rust//rust:defs.bzl", "rust_clippy")
+
+rust_clippy(
+    name = "hello_library_clippy",
+    testonly = True,
+    deps = [
+        ":hello_lib",
+        ":greeting_test",
+    ],
+)
+```
+""",
+)
+
+def _capture_clippy_output_impl(ctx):
+    """Implementation of the `capture_clippy_output` rule
+
+    Args:
+        ctx (ctx): The rule's context object
+
+    Returns:
+        list: A list containing the CaptureClippyOutputInfo provider
+    """
+    return [CaptureClippyOutputInfo(capture_output = ctx.build_setting_value)]
+
+capture_clippy_output = rule(
+    doc = "Control whether to print clippy output or store it to a file, using the configured error_format.",
+    implementation = _capture_clippy_output_impl,
+    build_setting = config.bool(flag = True),
+)