Skip to content

Commit 0e7915d

Browse files
committed
Auto merge of #96085 - jsgf:deny-unused-deps, r=compiler-errors
Make sure `-Dunused-crate-dependencies --json unused-externs` makes rustc exit with error status This PR: - fixes compiletest to understand unused extern notifications - adds tests for `--json unused-externs` - makes sure that deny-level unused externs notifications are treated as compile errors - refactors the `emit_unused_externs` callstack to plumb through the level as an enum as a string, and adds `Level::is_error` Update: adds `--json unused-externs-silent` with the original behaviour since Cargo needs it. Should address `@est31's` concerns. Fixes: #96068
2 parents 81799cd + c6bafa7 commit 0e7915d

File tree

20 files changed

+156
-21
lines changed

20 files changed

+156
-21
lines changed

compiler/rustc_errors/src/emitter.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,12 @@ pub trait Emitter {
212212
fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
213213

214214
/// Emit list of unused externs
215-
fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
215+
fn emit_unused_externs(
216+
&mut self,
217+
_lint_level: rustc_lint_defs::Level,
218+
_unused_externs: &[&str],
219+
) {
220+
}
216221

217222
/// Checks if should show explanations about "rustc --explain"
218223
fn should_show_explain(&self) -> bool {

compiler/rustc_errors/src/json.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,8 @@ impl Emitter for JsonEmitter {
171171
}
172172
}
173173

174-
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
174+
fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
175+
let lint_level = lint_level.as_str();
175176
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
176177
let result = if self.pretty {
177178
writeln!(&mut self.dst, "{}", as_pretty_json(&data))

compiler/rustc_errors/src/lib.rs

+14-3
Original file line numberDiff line numberDiff line change
@@ -969,8 +969,19 @@ impl Handler {
969969
self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
970970
}
971971

972-
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
973-
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
972+
pub fn emit_unused_externs(
973+
&self,
974+
lint_level: rustc_lint_defs::Level,
975+
loud: bool,
976+
unused_externs: &[&str],
977+
) {
978+
let mut inner = self.inner.borrow_mut();
979+
980+
if loud && lint_level.is_error() {
981+
inner.bump_err_count();
982+
}
983+
984+
inner.emit_unused_externs(lint_level, unused_externs)
974985
}
975986

976987
pub fn update_unstable_expectation_id(
@@ -1141,7 +1152,7 @@ impl HandlerInner {
11411152
self.emitter.emit_artifact_notification(path, artifact_type);
11421153
}
11431154

1144-
fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
1155+
fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
11451156
self.emitter.emit_unused_externs(lint_level, unused_externs);
11461157
}
11471158

compiler/rustc_lint_defs/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,13 @@ impl Level {
214214
_ => None,
215215
}
216216
}
217+
218+
pub fn is_error(self) -> bool {
219+
match self {
220+
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn => false,
221+
Level::Deny | Level::Forbid => true,
222+
}
223+
}
217224
}
218225

219226
/// Specification of a single lint.

compiler/rustc_metadata/src/creader.rs

+9-6
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,12 @@ impl CStore {
195195
}
196196

197197
pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
198+
let json_unused_externs = tcx.sess.opts.json_unused_externs;
199+
198200
// We put the check for the option before the lint_level_at_node call
199201
// because the call mutates internal state and introducing it
200202
// leads to some ui tests failing.
201-
if !tcx.sess.opts.json_unused_externs {
203+
if !json_unused_externs.is_enabled() {
202204
return;
203205
}
204206
let level = tcx
@@ -208,10 +210,11 @@ impl CStore {
208210
let unused_externs =
209211
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
210212
let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
211-
tcx.sess
212-
.parse_sess
213-
.span_diagnostic
214-
.emit_unused_externs(level.as_str(), &unused_externs);
213+
tcx.sess.parse_sess.span_diagnostic.emit_unused_externs(
214+
level,
215+
json_unused_externs.is_loud(),
216+
&unused_externs,
217+
);
215218
}
216219
}
217220
}
@@ -917,7 +920,7 @@ impl<'a> CrateLoader<'a> {
917920
}
918921

919922
// Got a real unused --extern
920-
if self.sess.opts.json_unused_externs {
923+
if self.sess.opts.json_unused_externs.is_enabled() {
921924
self.cstore.unused_externs.push(name_interned);
922925
continue;
923926
}

compiler/rustc_session/src/config.rs

+33-5
Original file line numberDiff line numberDiff line change
@@ -757,7 +757,7 @@ impl Default for Options {
757757
real_rust_source_base_dir: None,
758758
edition: DEFAULT_EDITION,
759759
json_artifact_notifications: false,
760-
json_unused_externs: false,
760+
json_unused_externs: JsonUnusedExterns::No,
761761
json_future_incompat: false,
762762
pretty: None,
763763
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
@@ -1498,10 +1498,37 @@ pub fn parse_color(matches: &getopts::Matches) -> ColorConfig {
14981498
pub struct JsonConfig {
14991499
pub json_rendered: HumanReadableErrorType,
15001500
pub json_artifact_notifications: bool,
1501-
pub json_unused_externs: bool,
1501+
pub json_unused_externs: JsonUnusedExterns,
15021502
pub json_future_incompat: bool,
15031503
}
15041504

1505+
/// Report unused externs in event stream
1506+
#[derive(Copy, Clone)]
1507+
pub enum JsonUnusedExterns {
1508+
/// Do not
1509+
No,
1510+
/// Report, but do not exit with failure status for deny/forbid
1511+
Silent,
1512+
/// Report, and also exit with failure status for deny/forbid
1513+
Loud,
1514+
}
1515+
1516+
impl JsonUnusedExterns {
1517+
pub fn is_enabled(&self) -> bool {
1518+
match self {
1519+
JsonUnusedExterns::No => false,
1520+
JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
1521+
}
1522+
}
1523+
1524+
pub fn is_loud(&self) -> bool {
1525+
match self {
1526+
JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
1527+
JsonUnusedExterns::Loud => true,
1528+
}
1529+
}
1530+
}
1531+
15051532
/// Parse the `--json` flag.
15061533
///
15071534
/// The first value returned is how to render JSON diagnostics, and the second
@@ -1511,7 +1538,7 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
15111538
HumanReadableErrorType::Default;
15121539
let mut json_color = ColorConfig::Never;
15131540
let mut json_artifact_notifications = false;
1514-
let mut json_unused_externs = false;
1541+
let mut json_unused_externs = JsonUnusedExterns::No;
15151542
let mut json_future_incompat = false;
15161543
for option in matches.opt_strs("json") {
15171544
// For now conservatively forbid `--color` with `--json` since `--json`
@@ -1529,7 +1556,8 @@ pub fn parse_json(matches: &getopts::Matches) -> JsonConfig {
15291556
"diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
15301557
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
15311558
"artifacts" => json_artifact_notifications = true,
1532-
"unused-externs" => json_unused_externs = true,
1559+
"unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
1560+
"unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
15331561
"future-incompat" => json_future_incompat = true,
15341562
s => early_error(
15351563
ErrorOutputType::default(),
@@ -2229,7 +2257,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
22292257

22302258
check_debug_option_stability(&debugging_opts, error_format, json_rendered);
22312259

2232-
if !debugging_opts.unstable_options && json_unused_externs {
2260+
if !debugging_opts.unstable_options && json_unused_externs.is_enabled() {
22332261
early_error(
22342262
error_format,
22352263
"the `-Z unstable-options` flag must also be passed to enable \

compiler/rustc_session/src/options.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ top_level_options!(
221221
json_artifact_notifications: bool [TRACKED],
222222

223223
/// `true` if we're emitting a JSON blob containing the unused externs
224-
json_unused_externs: bool [UNTRACKED],
224+
json_unused_externs: JsonUnusedExterns [UNTRACKED],
225225

226226
/// `true` if we're emitting a JSON job containing a future-incompat report for lints
227227
json_future_incompat: bool [TRACKED],

src/librustdoc/config.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ use rustc_session::config::{
1010
self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType,
1111
};
1212
use rustc_session::config::{get_cmd_lint_options, nightly_options};
13-
use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
13+
use rustc_session::config::{
14+
CodegenOptions, DebuggingOptions, ErrorOutputType, Externs, JsonUnusedExterns,
15+
};
1416
use rustc_session::getopts;
1517
use rustc_session::lint::Level;
1618
use rustc_session::search_paths::SearchPath;
@@ -147,7 +149,7 @@ crate struct Options {
147149
/// documentation.
148150
crate run_check: bool,
149151
/// Whether doctests should emit unused externs
150-
crate json_unused_externs: bool,
152+
crate json_unused_externs: JsonUnusedExterns,
151153
/// Whether to skip capturing stdout and stderr of tests.
152154
crate nocapture: bool,
153155

src/librustdoc/doctest.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ crate fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> {
168168

169169
// Collect and warn about unused externs, but only if we've gotten
170170
// reports for each doctest
171-
if json_unused_externs {
171+
if json_unused_externs.is_enabled() {
172172
let unused_extern_reports: Vec<_> =
173173
std::mem::take(&mut unused_extern_reports.lock().unwrap());
174174
if unused_extern_reports.len() == compiling_test_count {
@@ -337,7 +337,7 @@ fn run_test(
337337
if lang_string.test_harness {
338338
compiler.arg("--test");
339339
}
340-
if rustdoc_options.json_unused_externs && !lang_string.compile_fail {
340+
if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail {
341341
compiler.arg("--error-format=json");
342342
compiler.arg("--json").arg("unused-externs");
343343
compiler.arg("-Z").arg("unstable-options");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Check for unused crate dep, no path
2+
3+
// edition:2018
4+
// aux-crate:bar=bar.rs
5+
6+
#![deny(unused_crate_dependencies)]
7+
//~^ ERROR external crate `bar` unused in
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error: external crate `bar` unused in `deny_attr`: remove the dependency or add `use bar as _;`
2+
--> $DIR/deny-attr.rs:6:1
3+
|
4+
LL | #![deny(unused_crate_dependencies)]
5+
| ^
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/deny-attr.rs:6:9
9+
|
10+
LL | #![deny(unused_crate_dependencies)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Check for unused crate dep, json event, deny but we're not reporting that in exit status
2+
3+
// edition:2018
4+
// check-pass
5+
// compile-flags: -Dunused-crate-dependencies -Zunstable-options --json unused-externs-silent --error-format=json
6+
// aux-crate:bar=bar.rs
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"lint_level":"deny","unused_extern_names":["bar"]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Check for unused crate dep, json event, deny, expect compile failure
2+
3+
// edition:2018
4+
// compile-flags: -Dunused-crate-dependencies -Zunstable-options --json unused-externs --error-format=json
5+
// aux-crate:bar=bar.rs
6+
7+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"lint_level":"deny","unused_extern_names":["bar"]}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Check for unused crate dep, deny, expect failure
2+
3+
// edition:2018
4+
// compile-flags: -Dunused-crate-dependencies
5+
// aux-crate:bar=bar.rs
6+
7+
fn main() {}
8+
//~^ ERROR external crate `bar` unused in
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: external crate `bar` unused in `deny_cmdline`: remove the dependency or add `use bar as _;`
2+
--> $DIR/deny-cmdline.rs:7:1
3+
|
4+
LL | fn main() {}
5+
| ^
6+
|
7+
= note: requested on the command line with `-D unused-crate-dependencies`
8+
9+
error: aborting due to previous error
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// Check for unused crate dep, warn, json event, expect pass
2+
3+
// edition:2018
4+
// check-pass
5+
// compile-flags: -Wunused-crate-dependencies -Zunstable-options --json unused-externs --error-format=json
6+
// aux-crate:bar=bar.rs
7+
8+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"lint_level":"warn","unused_extern_names":["bar"]}

src/tools/compiletest/src/json.rs

+11
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ struct ArtifactNotification {
2323
artifact: PathBuf,
2424
}
2525

26+
#[derive(Deserialize)]
27+
struct UnusedExternNotification {
28+
#[allow(dead_code)]
29+
lint_level: String,
30+
#[allow(dead_code)]
31+
unused_extern_names: Vec<String>,
32+
}
33+
2634
#[derive(Deserialize, Clone)]
2735
struct DiagnosticSpan {
2836
file_name: String,
@@ -113,6 +121,9 @@ pub fn extract_rendered(output: &str) -> String {
113121
} else if serde_json::from_str::<ArtifactNotification>(line).is_ok() {
114122
// Ignore the notification.
115123
None
124+
} else if serde_json::from_str::<UnusedExternNotification>(line).is_ok() {
125+
// Ignore the notification.
126+
None
116127
} else {
117128
print!(
118129
"failed to decode compiler output as json: line: {}\noutput: {}",

0 commit comments

Comments
 (0)