Skip to content

Commit 034f1fb

Browse files
committed
Fix handling of allow/deny/forbid(warnings)
1 parent 440b639 commit 034f1fb

13 files changed

+209
-29
lines changed

compiler/rustc_errors/src/diagnostic_builder.rs

+13-1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ struct DiagnosticBuilderInner<'a> {
2828
handler: &'a Handler,
2929
diagnostic: Diagnostic,
3030
allow_suggestions: bool,
31+
is_lint: bool,
3132
}
3233

3334
/// In general, the `DiagnosticBuilder` uses deref to allow access to
@@ -100,7 +101,7 @@ impl<'a> DerefMut for DiagnosticBuilder<'a> {
100101
impl<'a> DiagnosticBuilder<'a> {
101102
/// Emit the diagnostic.
102103
pub fn emit(&mut self) {
103-
self.0.handler.emit_diagnostic(&self);
104+
self.0.handler.emit_diagnostic_with_ignore(&self, self.0.is_lint);
104105
self.cancel();
105106
}
106107

@@ -373,6 +374,16 @@ impl<'a> DiagnosticBuilder<'a> {
373374
self
374375
}
375376

377+
/// Set by LintDiagnosticBuilder to distinguish lint warnings from other
378+
/// warnings. Necessary because `-A warnings` on the command line turns
379+
/// off all regular warnings, but lint warnings can be turned on again
380+
/// with #[warn(...)].
381+
pub fn is_lint(&mut self, value: bool) -> &mut Self {
382+
debug!("is_lint({:?})", value);
383+
self.0.is_lint = value;
384+
self
385+
}
386+
376387
/// Convenience function for internal use, clients should use one of the
377388
/// `struct_*` methods on [`Handler`].
378389
crate fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
@@ -399,6 +410,7 @@ impl<'a> DiagnosticBuilder<'a> {
399410
handler,
400411
diagnostic,
401412
allow_suggestions: true,
413+
is_lint: false,
402414
}))
403415
}
404416
}

compiler/rustc_errors/src/lib.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -553,11 +553,7 @@ impl Handler {
553553

554554
/// Construct a builder at the `Warning` level with the `msg`.
555555
pub fn struct_warn(&self, msg: &str) -> DiagnosticBuilder<'_> {
556-
let mut result = DiagnosticBuilder::new(self, Level::Warning, msg);
557-
if !self.flags.can_emit_warnings {
558-
result.cancel();
559-
}
560-
result
556+
DiagnosticBuilder::new(self, Level::Warning, msg)
561557
}
562558

563559
/// Construct a builder at the `Allow` level with the `msg`.
@@ -752,6 +748,14 @@ impl Handler {
752748
self.inner.borrow_mut().force_print_diagnostic(db)
753749
}
754750

751+
pub fn emit_diagnostic_with_ignore(
752+
&self,
753+
diagnostic: &Diagnostic,
754+
ignore_can_emit_warnings: bool,
755+
) {
756+
self.inner.borrow_mut().emit_diagnostic_with_ignore(diagnostic, ignore_can_emit_warnings)
757+
}
758+
755759
pub fn emit_diagnostic(&self, diagnostic: &Diagnostic) {
756760
self.inner.borrow_mut().emit_diagnostic(diagnostic)
757761
}
@@ -794,6 +798,14 @@ impl HandlerInner {
794798
}
795799

796800
fn emit_diagnostic(&mut self, diagnostic: &Diagnostic) {
801+
self.emit_diagnostic_with_ignore(diagnostic, false);
802+
}
803+
804+
fn emit_diagnostic_with_ignore(
805+
&mut self,
806+
diagnostic: &Diagnostic,
807+
ignore_can_emit_warnings: bool,
808+
) {
797809
if diagnostic.cancelled() {
798810
return;
799811
}
@@ -802,7 +814,8 @@ impl HandlerInner {
802814
self.future_breakage_diagnostics.push(diagnostic.clone());
803815
}
804816

805-
if diagnostic.level == Warning && !self.flags.can_emit_warnings {
817+
if diagnostic.level == Warning && !self.flags.can_emit_warnings && !ignore_can_emit_warnings
818+
{
806819
if diagnostic.has_future_breakage() {
807820
(*TRACK_DIAGNOSTICS)(diagnostic);
808821
}

compiler/rustc_lint/src/levels.rs

+40-10
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ impl<'s> LintLevelsBuilder<'s> {
8787
self.sets.list.push(LintSet::CommandLine { specs: FxHashMap::default() });
8888
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);
8989

90-
for &(ref lint_name, level) in &sess.opts.lint_opts {
91-
store.check_lint_name_cmdline(sess, &lint_name, Some(level));
90+
for (position, &(ref lint_name, level)) in (0u32..).zip(sess.opts.lint_opts.iter()) {
91+
store.check_lint_name_cmdline(sess, &lint_name, level);
9292
let orig_level = level;
9393

9494
// If the cap is less than this specified level, e.g., if we've got
@@ -97,14 +97,15 @@ impl<'s> LintLevelsBuilder<'s> {
9797
let level = cmp::min(level, self.sets.lint_cap);
9898

9999
let lint_flag_val = Symbol::intern(lint_name);
100+
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level, position);
100101

101102
let ids = match store.find_lints(&lint_name) {
102103
Ok(ids) => ids,
103104
Err(_) => continue, // errors handled in check_lint_name_cmdline above
104105
};
106+
105107
for id in ids {
106108
self.check_gated_lint(id, DUMMY_SP);
107-
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
108109
let specs = match self.sets.list.last().unwrap() {
109110
LintSet::CommandLine { ref specs } => specs,
110111
_ => unreachable!(),
@@ -132,6 +133,22 @@ impl<'s> LintLevelsBuilder<'s> {
132133
self.sets.list.push(LintSet::CommandLine { specs });
133134
}
134135

136+
fn get_current_depth(&self) -> u32 {
137+
let mut idx = self.cur;
138+
let mut depth = 0;
139+
loop {
140+
match &self.sets.list[idx as usize] {
141+
LintSet::CommandLine { .. } => {
142+
return depth;
143+
}
144+
LintSet::Node { parent, .. } => {
145+
depth += 1;
146+
idx = *parent;
147+
}
148+
}
149+
}
150+
}
151+
135152
fn check_before_insert_spec(
136153
&self,
137154
specs: &FxHashMap<LintId, LevelAndSource>,
@@ -157,8 +174,8 @@ impl<'s> LintLevelsBuilder<'s> {
157174
let id_name = id.lint.name_lower();
158175
let fcw_warning = match old_src {
159176
LintLevelSource::Default => false,
160-
LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol),
161-
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
177+
LintLevelSource::Node(symbol, ..) => self.store.is_lint_group(symbol),
178+
LintLevelSource::CommandLine(symbol, ..) => self.store.is_lint_group(symbol),
162179
LintLevelSource::ForceWarn(_symbol) => {
163180
bug!("forced warn lint returned a forbid lint level")
164181
}
@@ -177,13 +194,13 @@ impl<'s> LintLevelsBuilder<'s> {
177194
id.to_string()
178195
));
179196
}
180-
LintLevelSource::Node(_, forbid_source_span, reason) => {
197+
LintLevelSource::Node(_, forbid_source_span, reason, ..) => {
181198
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
182199
if let Some(rationale) = reason {
183200
diag_builder.note(&rationale.as_str());
184201
}
185202
}
186-
LintLevelSource::CommandLine(_, _) => {
203+
LintLevelSource::CommandLine(..) => {
187204
diag_builder.note("`forbid` lint level was set on command line");
188205
}
189206
_ => bug!("forced warn lint returned a forbid lint level"),
@@ -263,7 +280,7 @@ impl<'s> LintLevelsBuilder<'s> {
263280
let mut specs = FxHashMap::default();
264281
let sess = self.sess;
265282
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
266-
for attr in attrs {
283+
for (attr_pos, attr) in (0u32..).zip(attrs.iter()) {
267284
let level = match Level::from_symbol(attr.name_or_empty()) {
268285
None => continue,
269286
Some(lvl) => lvl,
@@ -374,7 +391,10 @@ impl<'s> LintLevelsBuilder<'s> {
374391
meta_item.path.segments.last().expect("empty lint name").ident.name,
375392
sp,
376393
reason,
394+
self.get_current_depth(),
395+
attr_pos,
377396
);
397+
378398
for &id in *ids {
379399
self.check_gated_lint(id, attr.span);
380400
self.insert_spec(&mut specs, id, (level, src));
@@ -389,6 +409,8 @@ impl<'s> LintLevelsBuilder<'s> {
389409
Symbol::intern(complete_name),
390410
sp,
391411
reason,
412+
self.get_current_depth(),
413+
attr_pos,
392414
);
393415
for id in ids {
394416
self.insert_spec(&mut specs, *id, (level, src));
@@ -425,6 +447,8 @@ impl<'s> LintLevelsBuilder<'s> {
425447
Symbol::intern(&new_lint_name),
426448
sp,
427449
reason,
450+
self.get_current_depth(),
451+
attr_pos,
428452
);
429453
for id in ids {
430454
self.insert_spec(&mut specs, *id, (level, src));
@@ -495,7 +519,13 @@ impl<'s> LintLevelsBuilder<'s> {
495519
// Ignore any errors or warnings that happen because the new name is inaccurate
496520
// NOTE: `new_name` already includes the tool name, so we don't have to add it again.
497521
if let CheckLintNameResult::Ok(ids) = store.check_lint_name(&new_name, None) {
498-
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
522+
let src = LintLevelSource::Node(
523+
Symbol::intern(&new_name),
524+
sp,
525+
reason,
526+
self.get_current_depth(),
527+
attr_pos,
528+
);
499529
for &id in ids {
500530
self.check_gated_lint(id, attr.span);
501531
self.insert_spec(&mut specs, id, (level, src));
@@ -514,7 +544,7 @@ impl<'s> LintLevelsBuilder<'s> {
514544
}
515545

516546
let (lint_attr_name, lint_attr_span) = match *src {
517-
LintLevelSource::Node(name, span, _) => (name, span),
547+
LintLevelSource::Node(name, span, _, _, _) => (name, span),
518548
_ => continue,
519549
};
520550

compiler/rustc_middle/src/lint.rs

+41-11
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,18 @@ pub enum LintLevelSource {
2222
Default,
2323

2424
/// Lint level was set by an attribute.
25-
Node(Symbol, Span, Option<Symbol> /* RFC 2383 reason */),
25+
Node(
26+
Symbol,
27+
Span,
28+
Option<Symbol>, /* RFC 2383 reason */
29+
u32, /* depth */
30+
u32, /* position */
31+
),
2632

2733
/// Lint level was set by a command-line flag.
2834
/// The provided `Level` is the level specified on the command line.
2935
/// (The actual level may be lower due to `--cap-lints`.)
30-
CommandLine(Symbol, Level),
36+
CommandLine(Symbol, Level, u32 /* position */),
3137

3238
/// Lint is being forced to warn no matter what.
3339
ForceWarn(Symbol),
@@ -37,20 +43,41 @@ impl LintLevelSource {
3743
pub fn name(&self) -> Symbol {
3844
match *self {
3945
LintLevelSource::Default => symbol::kw::Default,
40-
LintLevelSource::Node(name, _, _) => name,
41-
LintLevelSource::CommandLine(name, _) => name,
46+
LintLevelSource::Node(name, ..) => name,
47+
LintLevelSource::CommandLine(name, ..) => name,
4248
LintLevelSource::ForceWarn(name) => name,
4349
}
4450
}
4551

4652
pub fn span(&self) -> Span {
4753
match *self {
4854
LintLevelSource::Default => DUMMY_SP,
49-
LintLevelSource::Node(_, span, _) => span,
50-
LintLevelSource::CommandLine(_, _) => DUMMY_SP,
55+
LintLevelSource::Node(_, span, ..) => span,
56+
LintLevelSource::CommandLine(..) => DUMMY_SP,
5157
LintLevelSource::ForceWarn(_) => DUMMY_SP,
5258
}
5359
}
60+
61+
/// Whether a lint level specified at `a` can be overridden by one at `b`.
62+
pub fn can_override(a: &Self, b: &Self) -> bool {
63+
match (a, b) {
64+
(LintLevelSource::Default, LintLevelSource::Default) => false,
65+
(LintLevelSource::ForceWarn(_), LintLevelSource::ForceWarn(_)) => false,
66+
(_, LintLevelSource::ForceWarn(_)) => true,
67+
(LintLevelSource::ForceWarn(_), _) => false,
68+
(LintLevelSource::Default, _) => true,
69+
(_, LintLevelSource::Default) => false,
70+
(LintLevelSource::CommandLine(.., pos_a), LintLevelSource::CommandLine(.., pos_b)) => {
71+
pos_a < pos_b
72+
}
73+
(LintLevelSource::CommandLine(..), LintLevelSource::Node(..)) => true,
74+
(LintLevelSource::Node(..), LintLevelSource::CommandLine(..)) => false,
75+
(
76+
LintLevelSource::Node(.., depth_a, pos_a),
77+
LintLevelSource::Node(.., depth_b, pos_b),
78+
) => (depth_a < depth_b) || (depth_a == depth_b && pos_a < pos_b),
79+
}
80+
}
5481
}
5582

5683
/// A tuple of a lint level and its source.
@@ -116,9 +143,11 @@ impl LintLevelSets {
116143
let (warnings_level, warnings_src) =
117144
self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux);
118145
if let Some(configured_warning_level) = warnings_level {
119-
if configured_warning_level != Level::Warn {
120-
level = configured_warning_level;
121-
src = warnings_src;
146+
if LintLevelSource::can_override(&src, &warnings_src) {
147+
if configured_warning_level != Level::Warn {
148+
level = configured_warning_level;
149+
src = warnings_src;
150+
}
122151
}
123152
}
124153
}
@@ -227,6 +256,7 @@ impl<'a> LintDiagnosticBuilder<'a> {
227256
/// Return the inner DiagnosticBuilder, first setting the primary message to `msg`.
228257
pub fn build(mut self, msg: &str) -> DiagnosticBuilder<'a> {
229258
self.0.set_primary_message(msg);
259+
self.0.is_lint(true);
230260
self.0
231261
}
232262

@@ -310,7 +340,7 @@ pub fn struct_lint_level<'s, 'd>(
310340
&format!("`#[{}({})]` on by default", level.as_str(), name),
311341
);
312342
}
313-
LintLevelSource::CommandLine(lint_flag_val, orig_level) => {
343+
LintLevelSource::CommandLine(lint_flag_val, orig_level, _) => {
314344
let flag = match orig_level {
315345
Level::Warn => "-W",
316346
Level::Deny => "-D",
@@ -339,7 +369,7 @@ pub fn struct_lint_level<'s, 'd>(
339369
);
340370
}
341371
}
342-
LintLevelSource::Node(lint_attr_name, src, reason) => {
372+
LintLevelSource::Node(lint_attr_name, src, reason, _, _) => {
343373
if let Some(rationale) = reason {
344374
err.note(&rationale.as_str());
345375
}

compiler/rustc_session/src/session.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -1266,8 +1266,11 @@ pub fn build_session(
12661266
.lint_opts
12671267
.iter()
12681268
.filter(|&&(ref key, _)| *key == "warnings")
1269-
.map(|&(_, ref level)| *level == lint::Allow)
1269+
// We are only interested in the last one, because there might be a chain
1270+
// of `-A warnings -D warnings -W warnings ...`, and only the last one
1271+
// should have an effect
12701272
.last()
1273+
.map(|&(_, ref level)| *level == lint::Allow)
12711274
.unwrap_or(false);
12721275
let cap_lints_allow = sopts.lint_cap.map_or(false, |cap| cap == lint::Allow);
12731276
let can_emit_warnings = !(warnings_allow || cap_lints_allow);

src/test/ui/lint/issue-41941.rs

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Checks whether exceptions can be added to #![deny(warnings)].
2+
// check-pass
3+
4+
#![deny(warnings)]
5+
#![warn(unused_variables)]
6+
7+
fn main() {
8+
let a = 5;
9+
//~^ WARNING: unused
10+
}

src/test/ui/lint/issue-41941.stderr

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
warning: unused variable: `a`
2+
--> $DIR/issue-41941.rs:8:9
3+
|
4+
LL | let a = 5;
5+
| ^ help: if this is intentional, prefix it with an underscore: `_a`
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/issue-41941.rs:5:9
9+
|
10+
LL | #![warn(unused_variables)]
11+
| ^^^^^^^^^^^^^^^^
12+
13+
warning: 1 warning emitted
14+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// compile-flags: -Z deduplicate-diagnostics=yes -F unused -D forbidden_lint_groups -A unused
2+
//~^ ERROR: allow(unused) incompatible
3+
//~| WARNING: this was previously accepted
4+
//~| ERROR: allow(unused) incompatible
5+
//~| WARNING: this was previously accepted
6+
7+
fn main() {}

0 commit comments

Comments
 (0)