From 2577f6819c17ff3e930cae5e5e7a67a1c735b0f1 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 24 Jun 2026 15:57:33 -0700 Subject: [PATCH 1/2] rustc: include proc-macro dep transitive rlibs in proc-macro link inputs When building a proc-macro, rustc passes all transitively-reachable rlibs to the linker (clang++). This includes rlibs that come through proc-macro dependency chains, even though those rlibs are already embedded in their parent proc-macro .so files. Under --remote_download_outputs=minimal, Bazel only downloads declared action inputs. The missing rlibs cause clang++ to fail with "no such file or directory" because they weren't declared as action inputs. Add a new `transitive_proc_macro_dep_outputs` field to DepInfo. Populate it with the transitive_crate_outputs of proc-macro deps (the ones skipped by the existing `if not is_proc_macro` guard). In collect_inputs, include this depset in the action inputs when building a proc-macro target. --- rust/private/providers.bzl | 3 ++- rust/private/rustc.bzl | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/rust/private/providers.bzl b/rust/private/providers.bzl index 880b5fd968..b8ee0fa8cd 100644 --- a/rust/private/providers.bzl +++ b/rust/private/providers.bzl @@ -60,7 +60,8 @@ DepInfo = provider( "direct_crates": "depset[AliasableDepInfo]", "link_search_path_files": "depset[File]: All transitive files containing search paths to pass to the linker", "transitive_build_infos": "depset[BuildInfo]", - "transitive_crate_outputs": "depset[File]: All transitive crate outputs.", + "transitive_crate_outputs": "depset[File]: All transitive crate outputs, excluding those from proc-macro dep chains.", + "transitive_proc_macro_dep_outputs": "depset[File]: Transitive crate outputs from proc-macro deps. Required as action inputs for proc-macro link actions because rustc passes all transitively-reachable rlibs to the linker.", "transitive_crates": "depset[CrateInfo]", "transitive_data": "depset[File]: Data of all transitive non-macro dependencies.", "transitive_metadata_outputs": "depset[File]: All transitive metadata dependencies (.rmeta, for crates that provide them) and all transitive object dependencies (.rlib) for crates that don't provide metadata.", diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 2d787c12ad..540a13db29 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -192,6 +192,7 @@ def collect_deps( direct_crate_outputs = [] transitive_crate_outputs = [] + transitive_proc_macro_dep_outputs = [] direct_metadata_outputs = [] transitive_metadata_outputs = [] @@ -257,6 +258,8 @@ def collect_deps( direct_crate_outputs.append(crate_info.output) if not is_proc_macro: transitive_crate_outputs.append(dep_info.transitive_crate_outputs) + else: + transitive_proc_macro_dep_outputs.append(dep_info.transitive_crate_outputs) if not is_proc_macro: transitive_noncrates.append(dep_info.transitive_noncrates) @@ -302,6 +305,9 @@ def collect_deps( direct_crate_outputs, transitive = transitive_crate_outputs, ), + transitive_proc_macro_dep_outputs = depset( + transitive = transitive_proc_macro_dep_outputs, + ), transitive_metadata_outputs = depset( direct_metadata_outputs, transitive = transitive_metadata_outputs, @@ -766,6 +772,16 @@ def collect_inputs( if _depend_on_metadata(crate_info, force_depend_on_objects): transitive_crate_outputs = dep_info.transitive_metadata_outputs + # Proc-macro link actions invoke the linker against all transitively-reachable + # rlibs, including those that come through proc-macro dep chains. Under + # --remote_download_outputs=minimal, only declared action inputs are downloaded, + # so we must include them here even though they're excluded from the normal + # transitive_crate_outputs (which non-proc-macro callers don't need). + if _is_proc_macro(crate_info): + transitive_crate_outputs = depset( + transitive = [transitive_crate_outputs, dep_info.transitive_proc_macro_dep_outputs], + ) + nolinkstamp_compile_direct_inputs = [] if build_info: if build_info.rustc_env: From 729e90b38c97a8375bbebd0338ed34adb4e20686 Mon Sep 17 00:00:00 2001 From: Walter Gray Date: Wed, 24 Jun 2026 16:46:06 -0700 Subject: [PATCH 2/2] rustc: propagate nested proc-macro dep outputs --- rust/private/rustc.bzl | 1 + .../proc_macro/leaks_deps/nested/helper.rs | 3 + .../leaks_deps/nested/inner_macro.rs | 9 +++ .../leaks_deps/nested/middle_macro.rs | 8 +++ .../leaks_deps/nested/outer_macro.rs | 8 +++ .../proc_macro_does_not_leak_deps.bzl | 59 +++++++++++++++++++ 6 files changed, 88 insertions(+) create mode 100644 test/unit/proc_macro/leaks_deps/nested/helper.rs create mode 100644 test/unit/proc_macro/leaks_deps/nested/inner_macro.rs create mode 100644 test/unit/proc_macro/leaks_deps/nested/middle_macro.rs create mode 100644 test/unit/proc_macro/leaks_deps/nested/outer_macro.rs diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl index 540a13db29..b8c55c5567 100644 --- a/rust/private/rustc.bzl +++ b/rust/private/rustc.bzl @@ -260,6 +260,7 @@ def collect_deps( transitive_crate_outputs.append(dep_info.transitive_crate_outputs) else: transitive_proc_macro_dep_outputs.append(dep_info.transitive_crate_outputs) + transitive_proc_macro_dep_outputs.append(dep_info.transitive_proc_macro_dep_outputs) if not is_proc_macro: transitive_noncrates.append(dep_info.transitive_noncrates) diff --git a/test/unit/proc_macro/leaks_deps/nested/helper.rs b/test/unit/proc_macro/leaks_deps/nested/helper.rs new file mode 100644 index 0000000000..ea3c48d12a --- /dev/null +++ b/test/unit/proc_macro/leaks_deps/nested/helper.rs @@ -0,0 +1,3 @@ +pub fn value() -> u32 { + 42 +} diff --git a/test/unit/proc_macro/leaks_deps/nested/inner_macro.rs b/test/unit/proc_macro/leaks_deps/nested/inner_macro.rs new file mode 100644 index 0000000000..86f794b845 --- /dev/null +++ b/test/unit/proc_macro/leaks_deps/nested/inner_macro.rs @@ -0,0 +1,9 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn inner(input: TokenStream) -> TokenStream { + let _ = nested_helper::value(); + input +} diff --git a/test/unit/proc_macro/leaks_deps/nested/middle_macro.rs b/test/unit/proc_macro/leaks_deps/nested/middle_macro.rs new file mode 100644 index 0000000000..77eaddbb60 --- /dev/null +++ b/test/unit/proc_macro/leaks_deps/nested/middle_macro.rs @@ -0,0 +1,8 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn middle(input: TokenStream) -> TokenStream { + input +} diff --git a/test/unit/proc_macro/leaks_deps/nested/outer_macro.rs b/test/unit/proc_macro/leaks_deps/nested/outer_macro.rs new file mode 100644 index 0000000000..d64c5dc3e6 --- /dev/null +++ b/test/unit/proc_macro/leaks_deps/nested/outer_macro.rs @@ -0,0 +1,8 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +#[proc_macro] +pub fn outer(input: TokenStream) -> TokenStream { + input +} diff --git a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl index 6e7723c631..bd40496cc1 100644 --- a/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl +++ b/test/unit/proc_macro/leaks_deps/proc_macro_does_not_leak_deps.bzl @@ -148,6 +148,63 @@ proc_macro_does_not_leak_lib_deps_test = analysistest.make( }, ) +def _nested_proc_macro_dep_outputs_are_inputs_impl(ctx): + env = analysistest.begin(ctx) + actions = analysistest.target_under_test(env).actions + rustc_action = None + for action in actions: + if action.mnemonic == "Rustc": + rustc_action = action + break + + asserts.false(env, rustc_action == None) + + nested_helper_rlibs = [ + i + for i in rustc_action.inputs.to_list() + if "nested_helper" in i.path and i.extension == "rlib" + ] + asserts.equals(env, 1, len(nested_helper_rlibs)) + + return analysistest.end(env) + +def _nested_proc_macro_dep_outputs_are_inputs_test(): + rust_library( + name = "nested_helper", + srcs = ["leaks_deps/nested/helper.rs"], + edition = "2018", + ) + + rust_proc_macro( + name = "nested_inner_macro", + srcs = ["leaks_deps/nested/inner_macro.rs"], + edition = "2018", + deps = [":nested_helper"], + ) + + rust_proc_macro( + name = "nested_middle_macro", + srcs = ["leaks_deps/nested/middle_macro.rs"], + edition = "2018", + deps = [":nested_inner_macro"], + ) + + rust_proc_macro( + name = "nested_outer_macro", + srcs = ["leaks_deps/nested/outer_macro.rs"], + edition = "2018", + deps = [":nested_middle_macro"], + ) + + nested_proc_macro_dep_outputs_are_inputs_test( + name = "nested_proc_macro_dep_outputs_are_inputs_test", + target_under_test = ":nested_outer_macro", + ) + +nested_proc_macro_dep_outputs_are_inputs_test = analysistest.make( + _nested_proc_macro_dep_outputs_are_inputs_impl, +) + def proc_macro_does_not_leak_deps_test_suite(name): """Entry-point macro called from the BUILD file. @@ -156,10 +213,12 @@ def proc_macro_does_not_leak_deps_test_suite(name): """ _proc_macro_does_not_leak_deps_test() _proc_macro_does_not_leak_lib_deps_test() + _nested_proc_macro_dep_outputs_are_inputs_test() native.test_suite( name = name, tests = [ + ":nested_proc_macro_dep_outputs_are_inputs_test", ":proc_macro_does_not_leak_deps_test", ":proc_macro_does_not_leak_lib_deps_test", ],