Skip to content

Commit 595088d

Browse files
committed
Auto merge of #85788 - rylev:force-warns, r=nikomatsakis
Support for force-warns Implements #85512. This PR adds a new command line option `force-warns` which will force the provided lints to warn even if they are allowed by some other mechanism such as `#![allow(warnings)]`. Some remaining issues: * #85512 mentions that `force-warns` should also be capable of taking lint groups instead of individual lints. This is not implemented. * If a lint has a higher warning level than `warn`, this will cause that lint to warn instead. We probably want to allow the lint to error if it is set to a higher lint and is not allowed somewhere else. * One test is currently ignored because it's not working - when a deny-by-default lint is allowed, it does not currently warn under `force-warns`. I'm not sure why, but I wanted to get this in before the weekend. r? `@nikomatsakis`
2 parents 289ada5 + 896898e commit 595088d

28 files changed

+321
-23
lines changed

Diff for: compiler/rustc_lint/src/context.rs

+24-13
Original file line numberDiff line numberDiff line change
@@ -334,8 +334,14 @@ impl LintStore {
334334
}
335335
}
336336

337-
/// Checks the validity of lint names derived from the command line
338-
pub fn check_lint_name_cmdline(&self, sess: &Session, lint_name: &str, level: Level) {
337+
/// Checks the validity of lint names derived from the command line. Returns
338+
/// true if the lint is valid, false otherwise.
339+
pub fn check_lint_name_cmdline(
340+
&self,
341+
sess: &Session,
342+
lint_name: &str,
343+
level: Option<Level>,
344+
) -> bool {
339345
let db = match self.check_lint_name(lint_name, None) {
340346
CheckLintNameResult::Ok(_) => None,
341347
CheckLintNameResult::Warning(ref msg, _) => Some(sess.struct_warn(msg)),
@@ -361,18 +367,23 @@ impl LintStore {
361367
};
362368

363369
if let Some(mut db) = db {
364-
let msg = format!(
365-
"requested on the command line with `{} {}`",
366-
match level {
367-
Level::Allow => "-A",
368-
Level::Warn => "-W",
369-
Level::Deny => "-D",
370-
Level::Forbid => "-F",
371-
},
372-
lint_name
373-
);
374-
db.note(&msg);
370+
if let Some(level) = level {
371+
let msg = format!(
372+
"requested on the command line with `{} {}`",
373+
match level {
374+
Level::Allow => "-A",
375+
Level::Warn => "-W",
376+
Level::Deny => "-D",
377+
Level::Forbid => "-F",
378+
},
379+
lint_name
380+
);
381+
db.note(&msg);
382+
}
375383
db.emit();
384+
false
385+
} else {
386+
true
376387
}
377388
}
378389

Diff for: compiler/rustc_lint/src/levels.rs

+15-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl<'s> LintLevelsBuilder<'s> {
8888
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
8989

9090
for &(ref lint_name, level) in &sess.opts.lint_opts {
91-
store.check_lint_name_cmdline(sess, &lint_name, level);
91+
store.check_lint_name_cmdline(sess, &lint_name, Some(level));
9292
let orig_level = level;
9393

9494
// If the cap is less than this specified level, e.g., if we've got
@@ -109,6 +109,16 @@ impl<'s> LintLevelsBuilder<'s> {
109109
}
110110
}
111111

112+
for lint_name in &sess.opts.force_warns {
113+
let valid = store.check_lint_name_cmdline(sess, lint_name, None);
114+
if valid {
115+
let lints = store
116+
.find_lints(lint_name)
117+
.unwrap_or_else(|_| bug!("A valid lint failed to produce a lint ids"));
118+
self.sets.force_warns.extend(&lints);
119+
}
120+
}
121+
112122
self.sets.list.push(LintSet::CommandLine { specs });
113123
}
114124

@@ -142,6 +152,9 @@ impl<'s> LintLevelsBuilder<'s> {
142152
LintLevelSource::Default => false,
143153
LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol),
144154
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
155+
LintLevelSource::ForceWarn(_symbol) => {
156+
bug!("forced warn lint returned a forbid lint level")
157+
}
145158
};
146159
debug!(
147160
"fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
@@ -166,6 +179,7 @@ impl<'s> LintLevelsBuilder<'s> {
166179
LintLevelSource::CommandLine(_, _) => {
167180
diag_builder.note("`forbid` lint level was set on command line");
168181
}
182+
_ => bug!("forced warn lint returned a forbid lint level"),
169183
}
170184
diag_builder.emit();
171185
};

Diff for: compiler/rustc_middle/src/lint.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::cmp;
22

33
use crate::ich::StableHashingContext;
4-
use rustc_data_structures::fx::FxHashMap;
4+
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
55
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
66
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
77
use rustc_hir::HirId;
@@ -28,6 +28,9 @@ pub enum LintLevelSource {
2828
/// The provided `Level` is the level specified on the command line.
2929
/// (The actual level may be lower due to `--cap-lints`.)
3030
CommandLine(Symbol, Level),
31+
32+
/// Lint is being forced to warn no matter what.
33+
ForceWarn(Symbol),
3134
}
3235

3336
impl LintLevelSource {
@@ -36,6 +39,7 @@ impl LintLevelSource {
3639
LintLevelSource::Default => symbol::kw::Default,
3740
LintLevelSource::Node(name, _, _) => name,
3841
LintLevelSource::CommandLine(name, _) => name,
42+
LintLevelSource::ForceWarn(name) => name,
3943
}
4044
}
4145

@@ -44,6 +48,7 @@ impl LintLevelSource {
4448
LintLevelSource::Default => DUMMY_SP,
4549
LintLevelSource::Node(_, span, _) => span,
4650
LintLevelSource::CommandLine(_, _) => DUMMY_SP,
51+
LintLevelSource::ForceWarn(_) => DUMMY_SP,
4752
}
4853
}
4954
}
@@ -55,6 +60,7 @@ pub type LevelAndSource = (Level, LintLevelSource);
5560
pub struct LintLevelSets {
5661
pub list: Vec<LintSet>,
5762
pub lint_cap: Level,
63+
pub force_warns: FxHashSet<LintId>,
5864
}
5965

6066
#[derive(Debug)]
@@ -73,7 +79,11 @@ pub enum LintSet {
7379

7480
impl LintLevelSets {
7581
pub fn new() -> Self {
76-
LintLevelSets { list: Vec::new(), lint_cap: Level::Forbid }
82+
LintLevelSets {
83+
list: Vec::new(),
84+
lint_cap: Level::Forbid,
85+
force_warns: FxHashSet::default(),
86+
}
7787
}
7888

7989
pub fn get_lint_level(
@@ -83,6 +93,11 @@ impl LintLevelSets {
8393
aux: Option<&FxHashMap<LintId, LevelAndSource>>,
8494
sess: &Session,
8595
) -> LevelAndSource {
96+
// Check whether we should always warn
97+
if self.force_warns.contains(&LintId::of(lint)) {
98+
return (Level::Warn, LintLevelSource::ForceWarn(Symbol::intern(lint.name)));
99+
}
100+
86101
let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
87102

88103
// If `level` is none then we actually assume the default level for this
@@ -176,11 +191,11 @@ impl LintLevelMap {
176191
impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
177192
#[inline]
178193
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
179-
let LintLevelMap { ref sets, ref id_to_set } = *self;
194+
let LintLevelMap { ref sets, ref id_to_set, .. } = *self;
180195

181196
id_to_set.hash_stable(hcx, hasher);
182197

183-
let LintLevelSets { ref list, lint_cap } = *sets;
198+
let LintLevelSets { ref list, lint_cap, .. } = *sets;
184199

185200
lint_cap.hash_stable(hcx, hasher);
186201

@@ -346,6 +361,13 @@ pub fn struct_lint_level<'s, 'd>(
346361
);
347362
}
348363
}
364+
LintLevelSource::ForceWarn(_) => {
365+
sess.diag_note_once(
366+
&mut err,
367+
DiagnosticMessageId::from(lint),
368+
"warning forced by `force-warns` commandline option",
369+
);
370+
}
349371
}
350372

351373
err.code(DiagnosticId::Lint { name, has_future_breakage });

Diff for: compiler/rustc_session/src/config.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -677,6 +677,7 @@ impl Default for Options {
677677
optimize: OptLevel::No,
678678
debuginfo: DebugInfo::None,
679679
lint_opts: Vec::new(),
680+
force_warns: Vec::new(),
680681
lint_cap: None,
681682
describe_lints: false,
682683
output_types: OutputTypes(BTreeMap::new()),
@@ -1092,6 +1093,13 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
10921093
level",
10931094
"LEVEL",
10941095
),
1096+
opt::multi_s(
1097+
"",
1098+
"force-warns",
1099+
"Specifiy lints that should warn even if \
1100+
they are allowed somewhere else",
1101+
"LINT",
1102+
),
10951103
opt::multi_s("C", "codegen", "Set a codegen option", "OPT[=VALUE]"),
10961104
opt::flag_s("V", "version", "Print version info and exit"),
10971105
opt::flag_s("v", "verbose", "Use verbose output"),
@@ -1156,7 +1164,8 @@ pub fn rustc_optgroups() -> Vec<RustcOptGroup> {
11561164
pub fn get_cmd_lint_options(
11571165
matches: &getopts::Matches,
11581166
error_format: ErrorOutputType,
1159-
) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>) {
1167+
debugging_opts: &DebuggingOptions,
1168+
) -> (Vec<(String, lint::Level)>, bool, Option<lint::Level>, Vec<String>) {
11601169
let mut lint_opts_with_position = vec![];
11611170
let mut describe_lints = false;
11621171

@@ -1189,7 +1198,18 @@ pub fn get_cmd_lint_options(
11891198
lint::Level::from_str(&cap)
11901199
.unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap)))
11911200
});
1192-
(lint_opts, describe_lints, lint_cap)
1201+
1202+
if !debugging_opts.unstable_options && matches.opt_present("force-warns") {
1203+
early_error(
1204+
error_format,
1205+
"the `-Z unstable-options` flag must also be passed to enable \
1206+
the flag `--force-warns=lints`",
1207+
);
1208+
}
1209+
1210+
let force_warns = matches.opt_strs("force-warns");
1211+
1212+
(lint_opts, describe_lints, lint_cap, force_warns)
11931213
}
11941214

11951215
/// Parses the `--color` flag.
@@ -1926,9 +1946,10 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
19261946
let crate_types = parse_crate_types_from_list(unparsed_crate_types)
19271947
.unwrap_or_else(|e| early_error(error_format, &e[..]));
19281948

1929-
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
1930-
19311949
let mut debugging_opts = DebuggingOptions::build(matches, error_format);
1950+
let (lint_opts, describe_lints, lint_cap, force_warns) =
1951+
get_cmd_lint_options(matches, error_format, &debugging_opts);
1952+
19321953
check_debug_option_stability(&debugging_opts, error_format, json_rendered);
19331954

19341955
if !debugging_opts.unstable_options && json_unused_externs {
@@ -2100,6 +2121,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
21002121
optimize: opt_level,
21012122
debuginfo,
21022123
lint_opts,
2124+
force_warns,
21032125
lint_cap,
21042126
describe_lints,
21052127
output_types,

Diff for: compiler/rustc_session/src/options.rs

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ top_level_options!(
130130
debuginfo: DebugInfo [TRACKED],
131131
lint_opts: Vec<(String, lint::Level)> [TRACKED],
132132
lint_cap: Option<lint::Level> [TRACKED],
133+
force_warns: Vec<String> [TRACKED],
133134
describe_lints: bool [UNTRACKED],
134135
output_types: OutputTypes [TRACKED],
135136
search_paths: Vec<SearchPath> [UNTRACKED],
+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# `force-warns`
2+
3+
The tracking issue for this feature is: [#85512](https://github.com/rust-lang/rust/issues/85512).
4+
5+
------------------------
6+
7+
This feature allows you to cause any lint to produce a warning even if the lint has a different level by default or another level is set somewhere else. For instance, the `force-warns` option can be used to make a lint (e.g., `dead_code`) produce a warning even if that lint is allowed in code with `#![allow(dead_code)]`.
8+
9+
## Example
10+
11+
```rust,ignore (partial-example)
12+
#![allow(dead_code)]
13+
14+
fn dead_function() {}
15+
// This would normally not produce a warning even though the
16+
// function is not used, because dead code is being allowed
17+
18+
fn main() {}
19+
```
20+
21+
We can force a warning to be produced by providing `--force-warns dead_code` to rustc.

Diff for: src/librustdoc/config.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -628,7 +628,8 @@ impl Options {
628628
let generate_redirect_map = matches.opt_present("generate-redirect-map");
629629
let show_type_layout = matches.opt_present("show-type-layout");
630630

631-
let (lint_opts, describe_lints, lint_cap) = get_cmd_lint_options(matches, error_format);
631+
let (lint_opts, describe_lints, lint_cap, _) =
632+
get_cmd_lint_options(matches, error_format, &debugging_opts);
632633

633634
Ok(Options {
634635
input,

Diff for: src/librustdoc/lib.rs

+8
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,14 @@ fn opts() -> Vec<RustcOptGroup> {
510510
"LEVEL",
511511
)
512512
}),
513+
unstable("force-warns", |o| {
514+
o.optopt(
515+
"",
516+
"force-warns",
517+
"Lints that will warn even if allowed somewhere else",
518+
"LINTS",
519+
)
520+
}),
513521
unstable("index-page", |o| {
514522
o.optopt("", "index-page", "Markdown file to be used as index page", "PATH")
515523
}),

Diff for: src/test/run-make/unstable-flag-required/Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22

33
all:
44
$(RUSTDOC) --output-format=json x.html 2>&1 | diff - output-format-json.stderr
5+
$(RUSTC) --force-warns dead_code x.rs 2>&1 | diff - force-warns.stderr
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
error: the `-Z unstable-options` flag must also be passed to enable the flag `--force-warns=lints`
2+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// compile-flags: --force-warns elided_lifetimes_in_paths
2+
// check-pass
3+
4+
struct Foo<'a> {
5+
x: &'a u32,
6+
}
7+
8+
fn foo(x: &Foo) {}
9+
//~^ WARN hidden lifetime parameters in types are deprecated
10+
11+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: hidden lifetime parameters in types are deprecated
2+
--> $DIR/force-allowed-by-default-lint.rs:8:12
3+
|
4+
LL | fn foo(x: &Foo) {}
5+
| ^^^- help: indicate the anonymous lifetime: `<'_>`
6+
|
7+
= note: warning forced by `force-warns` commandline option
8+
9+
warning: 1 warning emitted
10+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// compile-flags: --force-warns const_err
2+
// check-pass
3+
4+
#![allow(const_err)]
5+
const C: i32 = 1 / 0;
6+
//~^ WARN any use of this value will cause an error
7+
//~| WARN this was previously accepted by the compiler
8+
9+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
warning: any use of this value will cause an error
2+
--> $DIR/force-allowed-deny-by-default-lint.rs:5:16
3+
|
4+
LL | const C: i32 = 1 / 0;
5+
| ---------------^^^^^-
6+
| |
7+
| attempt to divide `1_i32` by zero
8+
|
9+
= note: warning forced by `force-warns` commandline option
10+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
11+
= note: for more information, see issue #71800 <https://github.com/rust-lang/rust/issues/71800>
12+
13+
warning: 1 warning emitted
14+

Diff for: src/test/ui/lint/force-warn/force-allowed-warning.rs

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// compile-flags: --force-warns dead_code
2+
// check-pass
3+
4+
#![allow(dead_code)]
5+
6+
fn dead_function() {}
7+
//~^ WARN function is never used
8+
9+
fn main() {}
+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
warning: function is never used: `dead_function`
2+
--> $DIR/force-allowed-warning.rs:6:4
3+
|
4+
LL | fn dead_function() {}
5+
| ^^^^^^^^^^^^^
6+
|
7+
= note: warning forced by `force-warns` commandline option
8+
9+
warning: 1 warning emitted
10+

0 commit comments

Comments
 (0)