Skip to content

Commit afa2a67

Browse files
pnkfelixMark-Simulacrum
authored andcommitted
Prevent forbid from being ignored if overriden at the same level.
That is, this changes `#[forbid(foo)] #[allow(foo)]` from allowing foo to forbidding foo.
1 parent b4e77d2 commit afa2a67

File tree

6 files changed

+144
-18
lines changed

6 files changed

+144
-18
lines changed

compiler/rustc_lint/src/levels.rs

+43-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use rustc_hir as hir;
1010
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
1111
use rustc_hir::{intravisit, HirId};
1212
use rustc_middle::hir::map::Map;
13+
use rustc_middle::lint::LevelSource;
1314
use rustc_middle::lint::LintDiagnosticBuilder;
1415
use rustc_middle::lint::{struct_lint_level, LintLevelMap, LintLevelSets, LintSet, LintSource};
1516
use rustc_middle::ty::query::Providers;
@@ -95,6 +96,44 @@ impl<'s> LintLevelsBuilder<'s> {
9596
self.sets.list.push(LintSet::CommandLine { specs });
9697
}
9798

99+
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
100+
/// (e.g. if a forbid was already inserted on the same scope), then emits a
101+
/// diagnostic with no change to `specs`.
102+
fn insert_spec(
103+
&mut self,
104+
specs: &mut FxHashMap<LintId, LevelSource>,
105+
id: LintId,
106+
(level, src): LevelSource,
107+
) {
108+
if let Some((old_level, old_src)) = specs.get(&id) {
109+
if old_level == &Level::Forbid && level != Level::Forbid {
110+
let mut diag_builder = struct_span_err!(
111+
self.sess,
112+
src.span(),
113+
E0453,
114+
"{}({}) incompatible with previous forbid in same scope",
115+
level.as_str(),
116+
src.name(),
117+
);
118+
match *old_src {
119+
LintSource::Default => {}
120+
LintSource::Node(_, forbid_source_span, reason) => {
121+
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
122+
if let Some(rationale) = reason {
123+
diag_builder.note(&rationale.as_str());
124+
}
125+
}
126+
LintSource::CommandLine(_) => {
127+
diag_builder.note("`forbid` lint level was set on command line");
128+
}
129+
}
130+
diag_builder.emit();
131+
return;
132+
}
133+
}
134+
specs.insert(id, (level, src));
135+
}
136+
98137
/// Pushes a list of AST lint attributes onto this context.
99138
///
100139
/// This function will return a `BuilderPush` object which should be passed
@@ -109,7 +148,7 @@ impl<'s> LintLevelsBuilder<'s> {
109148
/// `#[allow]`
110149
///
111150
/// Don't forget to call `pop`!
112-
pub fn push(
151+
pub(crate) fn push(
113152
&mut self,
114153
attrs: &[ast::Attribute],
115154
store: &LintStore,
@@ -221,7 +260,7 @@ impl<'s> LintLevelsBuilder<'s> {
221260
let src = LintSource::Node(name, li.span(), reason);
222261
for &id in ids {
223262
self.check_gated_lint(id, attr.span);
224-
specs.insert(id, (level, src));
263+
self.insert_spec(&mut specs, id, (level, src));
225264
}
226265
}
227266

@@ -235,7 +274,7 @@ impl<'s> LintLevelsBuilder<'s> {
235274
reason,
236275
);
237276
for id in ids {
238-
specs.insert(*id, (level, src));
277+
self.insert_spec(&mut specs, *id, (level, src));
239278
}
240279
}
241280
Err((Some(ids), new_lint_name)) => {
@@ -272,7 +311,7 @@ impl<'s> LintLevelsBuilder<'s> {
272311
reason,
273312
);
274313
for id in ids {
275-
specs.insert(*id, (level, src));
314+
self.insert_spec(&mut specs, *id, (level, src));
276315
}
277316
}
278317
Err((None, _)) => {

compiler/rustc_middle/src/lint.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use rustc_session::lint::{builtin, Level, Lint, LintId};
99
use rustc_session::{DiagnosticMessageId, Session};
1010
use rustc_span::hygiene::MacroKind;
1111
use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
12-
use rustc_span::{Span, Symbol};
12+
use rustc_span::{symbol, Span, Symbol, DUMMY_SP};
1313

1414
/// How a lint level was set.
1515
#[derive(Clone, Copy, PartialEq, Eq, HashStable)]
@@ -25,6 +25,24 @@ pub enum LintSource {
2525
CommandLine(Symbol),
2626
}
2727

28+
impl LintSource {
29+
pub fn name(&self) -> Symbol {
30+
match *self {
31+
LintSource::Default => symbol::kw::Default,
32+
LintSource::Node(name, _, _) => name,
33+
LintSource::CommandLine(name) => name,
34+
}
35+
}
36+
37+
pub fn span(&self) -> Span {
38+
match *self {
39+
LintSource::Default => DUMMY_SP,
40+
LintSource::Node(_, span, _) => span,
41+
LintSource::CommandLine(_) => DUMMY_SP,
42+
}
43+
}
44+
}
45+
2846
pub type LevelSource = (Level, LintSource);
2947

3048
pub struct LintLevelSets {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// This test is checking that you cannot override a `forbid` by adding in other
2+
// attributes later in the same scope. (We already ensure that you cannot
3+
// override it in nested scopes).
4+
5+
// If you turn off deduplicate diagnostics (which rustc turns on by default but
6+
// compiletest turns off when it runs ui tests), then the errors are
7+
// (unfortunately) repeated here because the checking is done as we read in the
8+
// errors, and curretly that happens two or three different times, depending on
9+
// compiler flags.
10+
//
11+
// I decided avoiding the redundant output was not worth the time in engineering
12+
// effort for bug like this, which 1. end users are unlikely to run into in the
13+
// first place, and 2. they won't see the redundant output anyway.
14+
15+
// compile-flags: -Z deduplicate-diagnostics=yes
16+
17+
fn forbid_first(num: i32) -> i32 {
18+
#![forbid(unused)]
19+
#![deny(unused)]
20+
//~^ ERROR: deny(unused) incompatible with previous forbid in same scope [E0453]
21+
#![warn(unused)]
22+
//~^ ERROR: warn(unused) incompatible with previous forbid in same scope [E0453]
23+
#![allow(unused)]
24+
//~^ ERROR: allow(unused) incompatible with previous forbid in same scope [E0453]
25+
26+
num * num
27+
}
28+
29+
fn forbid_last(num: i32) -> i32 {
30+
#![deny(unused)]
31+
#![warn(unused)]
32+
#![allow(unused)]
33+
#![forbid(unused)]
34+
35+
num * num
36+
}
37+
38+
fn forbid_multiple(num: i32) -> i32 {
39+
#![forbid(unused)]
40+
#![forbid(unused)]
41+
42+
num * num
43+
}
44+
45+
fn main() {
46+
forbid_first(10);
47+
forbid_last(10);
48+
forbid_multiple(10);
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
error[E0453]: deny(unused) incompatible with previous forbid in same scope
2+
--> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:19:13
3+
|
4+
LL | #![forbid(unused)]
5+
| ------ `forbid` level set here
6+
LL | #![deny(unused)]
7+
| ^^^^^^
8+
9+
error[E0453]: warn(unused) incompatible with previous forbid in same scope
10+
--> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:21:13
11+
|
12+
LL | #![forbid(unused)]
13+
| ------ `forbid` level set here
14+
...
15+
LL | #![warn(unused)]
16+
| ^^^^^^
17+
18+
error[E0453]: allow(unused) incompatible with previous forbid in same scope
19+
--> $DIR/issue-70819-dont-override-forbid-in-same-scope.rs:23:14
20+
|
21+
LL | #![forbid(unused)]
22+
| ------ `forbid` level set here
23+
...
24+
LL | #![allow(unused)]
25+
| ^^^^^^
26+
27+
error: aborting due to 3 previous errors
28+
29+
For more information about this error, try `rustc --explain E0453`.

src/tools/clippy/tests/ui/attrs.rs

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// Test that the whole restriction group is not enabled
44
#![warn(clippy::restriction)]
55
#![deny(clippy::restriction)]
6-
#![forbid(clippy::restriction)]
76
#![allow(clippy::missing_docs_in_private_items, clippy::panic, clippy::unreachable)]
87

98
#[inline(always)]

src/tools/clippy/tests/ui/attrs.stderr

+4-12
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
2-
--> $DIR/attrs.rs:9:1
2+
--> $DIR/attrs.rs:8:1
33
|
44
LL | #[inline(always)]
55
| ^^^^^^^^^^^^^^^^^
66
|
77
= note: `-D clippy::inline-always` implied by `-D warnings`
88

99
error: the since field must contain a semver-compliant version
10-
--> $DIR/attrs.rs:29:14
10+
--> $DIR/attrs.rs:28:14
1111
|
1212
LL | #[deprecated(since = "forever")]
1313
| ^^^^^^^^^^^^^^^^^
1414
|
1515
= note: `-D clippy::deprecated-semver` implied by `-D warnings`
1616

1717
error: the since field must contain a semver-compliant version
18-
--> $DIR/attrs.rs:32:14
18+
--> $DIR/attrs.rs:31:14
1919
|
2020
LL | #[deprecated(since = "1")]
2121
| ^^^^^^^^^^^
@@ -37,13 +37,5 @@ LL | #![deny(clippy::restriction)]
3737
|
3838
= help: try enabling only the lints you really need
3939

40-
error: restriction lints are not meant to be all enabled
41-
--> $DIR/attrs.rs:6:11
42-
|
43-
LL | #![forbid(clippy::restriction)]
44-
| ^^^^^^^^^^^^^^^^^^^
45-
|
46-
= help: try enabling only the lints you really need
47-
48-
error: aborting due to 6 previous errors
40+
error: aborting due to 5 previous errors
4941

0 commit comments

Comments
 (0)