Skip to content

Commit 36505e2

Browse files
authored
fix: ignore implicit std dependencies in unused-crate-dependencies lint (#16677)
fixes [152561](rust-lang/rust#152561) ### What does this PR try to solve? Cargo explicitly passes standard library crates (core, alloc, etc.) as extern dependencies when using `-Zbuild-std`, which causes "erroneous" warnings because these crates are not listed in the user's Cargo.toml. ### The working This was initially issued on the `rust` repo, for which i filed a PR, using `noprelude` to exclude linting, but it was correctly pointed out that this was not the correct approach. Following [Zulip](https://rust-lang.zulipchat.com/#narrow/channel/131828-t-compiler/topic/unused_crate_dependencies.20with.20cargo.20-Zbuild-std/with/575423436) discussions, I added a `nounused` flag to the `UnitDeps` struct which is similar in working to the `noprelude` flag. `--extern nounused:foo` already exists as an unstable compiler option. ### How to test and review this PR? 1. To demonstrate that the implicitly injected crates no longer trigger linting warnings, use this test command: `rustup run nightly cargo test build_std_does_not_warn_about_implicit_std_deps`. 2. Verify that `core`, `alloc`, `compiler_builtins` do not throw unused warnings. **Note:** this makes build-std stabilization blocked on rust-lang/rust#98400 which is blocked on rust-lang/rust#98405 (which I think is already a dependence of build-std) --- Currently, `nounused` is emitted only for implicitly injected standard library dependencies when using `-Zbuild-std`. If Cargo ever gains support for explicit builtin dependencies declared by users, it may be desirable to revisit whether `nounused` should apply in those cases. This PR does not change behavior for explicit dependencies.
2 parents d916207 + 71f88a1 commit 36505e2

5 files changed

Lines changed: 123 additions & 58 deletions

File tree

src/cargo/core/compiler/mod.rs

Lines changed: 62 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1874,68 +1874,75 @@ pub fn extern_args(
18741874
let no_embed_metadata = build_runner.bcx.gctx.cli_unstable().no_embed_metadata;
18751875

18761876
// Closure to add one dependency to `result`.
1877-
let mut link_to =
1878-
|dep: &UnitDep, extern_crate_name: InternedString, noprelude: bool| -> CargoResult<()> {
1879-
let mut value = OsString::new();
1880-
let mut opts = Vec::new();
1881-
let is_public_dependency_enabled = unit
1882-
.pkg
1883-
.manifest()
1884-
.unstable_features()
1885-
.require(Feature::public_dependency())
1886-
.is_ok()
1887-
|| build_runner.bcx.gctx.cli_unstable().public_dependency;
1888-
if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1889-
opts.push("priv");
1890-
*unstable_opts = true;
1891-
}
1892-
if noprelude {
1893-
opts.push("noprelude");
1894-
*unstable_opts = true;
1895-
}
1896-
if !opts.is_empty() {
1897-
value.push(opts.join(","));
1898-
value.push(":");
1899-
}
1900-
value.push(extern_crate_name.as_str());
1901-
value.push("=");
1902-
1903-
let mut pass = |file| {
1904-
let mut value = value.clone();
1905-
value.push(file);
1906-
result.push(OsString::from("--extern"));
1907-
result.push(value);
1908-
};
1877+
let mut link_to = |dep: &UnitDep,
1878+
extern_crate_name: InternedString,
1879+
noprelude: bool,
1880+
nounused: bool|
1881+
-> CargoResult<()> {
1882+
let mut value = OsString::new();
1883+
let mut opts = Vec::new();
1884+
let is_public_dependency_enabled = unit
1885+
.pkg
1886+
.manifest()
1887+
.unstable_features()
1888+
.require(Feature::public_dependency())
1889+
.is_ok()
1890+
|| build_runner.bcx.gctx.cli_unstable().public_dependency;
1891+
if !dep.public && unit.target.is_lib() && is_public_dependency_enabled {
1892+
opts.push("priv");
1893+
*unstable_opts = true;
1894+
}
1895+
if noprelude {
1896+
opts.push("noprelude");
1897+
*unstable_opts = true;
1898+
}
1899+
if nounused {
1900+
opts.push("nounused");
1901+
*unstable_opts = true;
1902+
}
1903+
if !opts.is_empty() {
1904+
value.push(opts.join(","));
1905+
value.push(":");
1906+
}
1907+
value.push(extern_crate_name.as_str());
1908+
value.push("=");
1909+
1910+
let mut pass = |file| {
1911+
let mut value = value.clone();
1912+
value.push(file);
1913+
result.push(OsString::from("--extern"));
1914+
result.push(value);
1915+
};
19091916

1910-
let outputs = build_runner.outputs(&dep.unit)?;
1917+
let outputs = build_runner.outputs(&dep.unit)?;
19111918

1912-
if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1913-
// Example: rlib dependency for an rlib, rmeta is all that is required.
1914-
let output = outputs
1915-
.iter()
1916-
.find(|output| output.flavor == FileFlavor::Rmeta)
1917-
.expect("failed to find rmeta dep for pipelined dep");
1918-
pass(&output.path);
1919-
} else {
1920-
// Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1921-
for output in outputs.iter() {
1922-
if output.flavor == FileFlavor::Linkable {
1923-
pass(&output.path);
1924-
}
1925-
// If we use -Zembed-metadata=no, we also need to pass the path to the
1926-
// corresponding .rmeta file to the linkable artifact, because the
1927-
// normal dependency (rlib) doesn't contain the full metadata.
1928-
else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1929-
pass(&output.path);
1930-
}
1919+
if build_runner.only_requires_rmeta(unit, &dep.unit) || dep.unit.mode.is_check() {
1920+
// Example: rlib dependency for an rlib, rmeta is all that is required.
1921+
let output = outputs
1922+
.iter()
1923+
.find(|output| output.flavor == FileFlavor::Rmeta)
1924+
.expect("failed to find rmeta dep for pipelined dep");
1925+
pass(&output.path);
1926+
} else {
1927+
// Example: a bin needs `rlib` for dependencies, it cannot use rmeta.
1928+
for output in outputs.iter() {
1929+
if output.flavor == FileFlavor::Linkable {
1930+
pass(&output.path);
1931+
}
1932+
// If we use -Zembed-metadata=no, we also need to pass the path to the
1933+
// corresponding .rmeta file to the linkable artifact, because the
1934+
// normal dependency (rlib) doesn't contain the full metadata.
1935+
else if no_embed_metadata && output.flavor == FileFlavor::Rmeta {
1936+
pass(&output.path);
19311937
}
19321938
}
1933-
Ok(())
1934-
};
1939+
}
1940+
Ok(())
1941+
};
19351942

19361943
for dep in deps {
19371944
if dep.unit.target.is_linkable() && !dep.unit.mode.is_doc() {
1938-
link_to(dep, dep.extern_crate_name, dep.noprelude)?;
1945+
link_to(dep, dep.extern_crate_name, dep.noprelude, dep.nounused)?;
19391946
}
19401947
}
19411948
if unit.target.proc_macro() {

src/cargo/core/compiler/unit_dependencies.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ fn attach_std_deps(
206206
// TODO: Does this `public` make sense?
207207
public: true,
208208
noprelude: true,
209+
nounused: true,
209210
}));
210211
found = true;
211212
}
@@ -911,6 +912,7 @@ fn new_unit_dep_with_profile(
911912
dep_name,
912913
public,
913914
noprelude: false,
915+
nounused: false,
914916
})
915917
}
916918

src/cargo/core/compiler/unit_graph.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ pub struct UnitDep {
4040
pub public: bool,
4141
/// If `true`, the dependency should not be added to Rust's prelude.
4242
pub noprelude: bool,
43+
/// If `true`, the dependency will not trigger Rust lint.
44+
pub nounused: bool,
4345
}
4446

4547
const VERSION: u32 = 1;
@@ -74,6 +76,9 @@ struct SerializedUnitDep {
7476
// This is only set on nightly since it is unstable.
7577
#[serde(skip_serializing_if = "Option::is_none")]
7678
noprelude: Option<bool>,
79+
// This is only set on nightly since it is unstable.
80+
#[serde(skip_serializing_if = "Option::is_none")]
81+
nounused: Option<bool>,
7782
// Intentionally not including `unit_for` because it is a low-level
7883
// internal detail that is mostly used for building the graph.
7984
}
@@ -101,16 +106,21 @@ pub fn emit_serialized_unit_graph(
101106
.iter()
102107
.map(|unit_dep| {
103108
// https://github.com/rust-lang/rust/issues/64260 when stabilized.
104-
let (public, noprelude) = if gctx.nightly_features_allowed {
105-
(Some(unit_dep.public), Some(unit_dep.noprelude))
109+
let (public, noprelude, nounused) = if gctx.nightly_features_allowed {
110+
(
111+
Some(unit_dep.public),
112+
Some(unit_dep.noprelude),
113+
Some(unit_dep.nounused),
114+
)
106115
} else {
107-
(None, None)
116+
(None, None, None)
108117
};
109118
SerializedUnitDep {
110119
index: indices[&unit_dep.unit],
111120
extern_crate_name: unit_dep.extern_crate_name,
112121
public,
113122
noprelude,
123+
nounused,
114124
}
115125
})
116126
.collect();

tests/build-std/main.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,49 @@ fn test_proc_macro() {
438438
.run();
439439
}
440440

441+
#[cargo_test(build_std_real)]
442+
fn build_std_does_not_warn_about_implicit_std_deps() {
443+
let p = project()
444+
.file(
445+
"Cargo.toml",
446+
r#"
447+
[package]
448+
name = "buildstd_test"
449+
version = "0.1.0"
450+
edition = "2021"
451+
452+
[dependencies]
453+
bar = { path = "bar" }
454+
"#,
455+
)
456+
.file("src/main.rs", "fn main() {}")
457+
.file(
458+
"bar/Cargo.toml",
459+
r#"
460+
[package]
461+
name = "bar"
462+
version = "0.1.0"
463+
edition = "2021"
464+
"#,
465+
)
466+
.file("bar/src/lib.rs", "")
467+
.build();
468+
469+
p.cargo("build")
470+
.build_std()
471+
.target_host()
472+
.env("RUSTFLAGS", "-W unused-crate-dependencies")
473+
.with_stderr_data(
474+
str![[r#"
475+
[WARNING] extern crate `bar` is unused in crate `buildstd_test`
476+
[WARNING] `buildstd_test` (bin "buildstd_test") generated 1 warning
477+
...
478+
"#]]
479+
.unordered(),
480+
)
481+
.run();
482+
}
483+
441484
#[cargo_test(build_std_real)]
442485
fn default_features_still_included_with_extra_build_std_features() {
443486
// This is a regression test to ensure when adding extra `build-std-features`,

tests/testsuite/unit_graph.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ fn simple() {
6161
"extern_crate_name": "b",
6262
"index": 1,
6363
"noprelude": false,
64+
"nounused": false,
6465
"public": false
6566
}
6667
],
@@ -106,6 +107,7 @@ fn simple() {
106107
"extern_crate_name": "c",
107108
"index": 2,
108109
"noprelude": false,
110+
"nounused": false,
109111
"public": false
110112
}
111113
],
@@ -189,6 +191,7 @@ fn simple() {
189191
"extern_crate_name": "a",
190192
"index": 0,
191193
"noprelude": false,
194+
"nounused": false,
192195
"public": false
193196
}
194197
],

0 commit comments

Comments
 (0)