Skip to content

Commit 3b03446

Browse files
m-ou-seehuss
authored andcommitted
Rollup merge of rust-lang#81556 - nikomatsakis:forbidden-lint-groups-lint, r=pnkfelix
introduce future-compatibility warning for forbidden lint groups We used to ignore `forbid(group)` scenarios completely. This changed in rust-lang#78864, but that led to a number of regressions (rust-lang#80988, rust-lang#81218). This PR introduces a future compatibility warning for the case where a group is forbidden but then an individual lint within that group is allowed. We now issue a FCW when we see the "allow", but permit it to take effect. r? ``@Mark-Simulacrum``
1 parent f6b8e2b commit 3b03446

19 files changed

+553
-55
lines changed

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

+15
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use rustc_session::SessionLintStore;
3939
use rustc_span::lev_distance::find_best_match_for_name;
4040
use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP};
4141
use rustc_target::abi::LayoutOf;
42+
use tracing::debug;
4243

4344
use std::cell::Cell;
4445
use std::slice;
@@ -335,6 +336,20 @@ impl LintStore {
335336
}
336337
}
337338

339+
/// True if this symbol represents a lint group name.
340+
pub fn is_lint_group(&self, lint_name: Symbol) -> bool {
341+
debug!(
342+
"is_lint_group(lint_name={:?}, lint_groups={:?})",
343+
lint_name,
344+
self.lint_groups.keys().collect::<Vec<_>>()
345+
);
346+
let lint_name_str = &*lint_name.as_str();
347+
self.lint_groups.contains_key(&lint_name_str) || {
348+
let warnings_name_str = crate::WARNINGS.name_lower();
349+
lint_name_str == &*warnings_name_str
350+
}
351+
}
352+
338353
/// Checks the name of a lint for its existence, and whether it was
339354
/// renamed or removed. Generates a DiagnosticBuilder containing a
340355
/// warning for renamed and removed lints. This is over both lint

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

+73-28
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use rustc_ast::attr;
55
use rustc_ast::unwrap_or;
66
use rustc_ast_pretty::pprust;
77
use rustc_data_structures::fx::FxHashMap;
8-
use rustc_errors::{struct_span_err, Applicability};
8+
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder};
99
use rustc_hir as hir;
1010
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
1111
use rustc_hir::{intravisit, HirId};
@@ -17,11 +17,15 @@ use rustc_middle::lint::{
1717
};
1818
use rustc_middle::ty::query::Providers;
1919
use rustc_middle::ty::TyCtxt;
20-
use rustc_session::lint::{builtin, Level, Lint, LintId};
20+
use rustc_session::lint::{
21+
builtin::{self, FORBIDDEN_LINT_GROUPS},
22+
Level, Lint, LintId,
23+
};
2124
use rustc_session::parse::feature_err;
2225
use rustc_session::Session;
2326
use rustc_span::symbol::{sym, Symbol};
2427
use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP};
28+
use tracing::debug;
2529

2630
use std::cmp;
2731

@@ -51,6 +55,7 @@ pub struct LintLevelsBuilder<'s> {
5155
id_to_set: FxHashMap<HirId, u32>,
5256
cur: u32,
5357
warn_about_weird_lints: bool,
58+
store: &'s LintStore,
5459
}
5560

5661
pub struct BuilderPush {
@@ -59,13 +64,14 @@ pub struct BuilderPush {
5964
}
6065

6166
impl<'s> LintLevelsBuilder<'s> {
62-
pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &LintStore) -> Self {
67+
pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore) -> Self {
6368
let mut builder = LintLevelsBuilder {
6469
sess,
6570
sets: LintLevelSets::new(),
6671
cur: 0,
6772
id_to_set: Default::default(),
6873
warn_about_weird_lints,
74+
store,
6975
};
7076
builder.process_command_line(sess, store);
7177
assert_eq!(builder.sets.list.len(), 1);
@@ -120,36 +126,75 @@ impl<'s> LintLevelsBuilder<'s> {
120126
if let (Level::Forbid, old_src) =
121127
self.sets.get_lint_level(id.lint, self.cur, Some(&specs), &self.sess)
122128
{
123-
let mut diag_builder = struct_span_err!(
124-
self.sess,
125-
src.span(),
126-
E0453,
127-
"{}({}) incompatible with previous forbid",
128-
level.as_str(),
129-
src.name(),
129+
// Backwards compatibility check:
130+
//
131+
// We used to not consider `forbid(lint_group)`
132+
// as preventing `allow(lint)` for some lint `lint` in
133+
// `lint_group`. For now, issue a future-compatibility
134+
// warning for this case.
135+
let id_name = id.lint.name_lower();
136+
let fcw_warning = match old_src {
137+
LintLevelSource::Default => false,
138+
LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol),
139+
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
140+
};
141+
debug!(
142+
"fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
143+
fcw_warning, specs, old_src, id_name
130144
);
131-
diag_builder.span_label(src.span(), "overruled by previous forbid");
132-
match old_src {
133-
LintLevelSource::Default => {
134-
diag_builder.note(&format!(
135-
"`forbid` lint level is the default for {}",
136-
id.to_string()
137-
));
138-
}
139-
LintLevelSource::Node(_, forbid_source_span, reason) => {
140-
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
141-
if let Some(rationale) = reason {
142-
diag_builder.note(&rationale.as_str());
145+
146+
let decorate_diag_builder = |mut diag_builder: DiagnosticBuilder<'_>| {
147+
diag_builder.span_label(src.span(), "overruled by previous forbid");
148+
match old_src {
149+
LintLevelSource::Default => {
150+
diag_builder.note(&format!(
151+
"`forbid` lint level is the default for {}",
152+
id.to_string()
153+
));
154+
}
155+
LintLevelSource::Node(_, forbid_source_span, reason) => {
156+
diag_builder.span_label(forbid_source_span, "`forbid` level set here");
157+
if let Some(rationale) = reason {
158+
diag_builder.note(&rationale.as_str());
159+
}
160+
}
161+
LintLevelSource::CommandLine(_, _) => {
162+
diag_builder.note("`forbid` lint level was set on command line");
143163
}
144164
}
145-
LintLevelSource::CommandLine(_, _) => {
146-
diag_builder.note("`forbid` lint level was set on command line");
147-
}
165+
diag_builder.emit();
166+
};
167+
if !fcw_warning {
168+
let diag_builder = struct_span_err!(
169+
self.sess,
170+
src.span(),
171+
E0453,
172+
"{}({}) incompatible with previous forbid",
173+
level.as_str(),
174+
src.name(),
175+
);
176+
decorate_diag_builder(diag_builder);
177+
} else {
178+
self.struct_lint(
179+
FORBIDDEN_LINT_GROUPS,
180+
Some(src.span().into()),
181+
|diag_builder| {
182+
let diag_builder = diag_builder.build(&format!(
183+
"{}({}) incompatible with previous forbid",
184+
level.as_str(),
185+
src.name(),
186+
));
187+
decorate_diag_builder(diag_builder);
188+
},
189+
);
148190
}
149-
diag_builder.emit();
150191

151-
// Retain the forbid lint level
152-
return;
192+
// Retain the forbid lint level, unless we are
193+
// issuing a FCW. In the FCW case, we want to
194+
// respect the new setting.
195+
if !fcw_warning {
196+
return;
197+
}
153198
}
154199
}
155200
specs.insert(id, (level, src));

Diff for: compiler/rustc_lint_defs/src/builtin.rs

+37
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,42 @@ use crate::{declare_lint, declare_lint_pass, declare_tool_lint};
88
use rustc_span::edition::Edition;
99
use rustc_span::symbol::sym;
1010

11+
declare_lint! {
12+
/// The `forbidden_lint_groups` lint detects violations of
13+
/// `forbid` applied to a lint group. Due to a bug in the compiler,
14+
/// these used to be overlooked entirely. They now generate a warning.
15+
///
16+
/// ### Example
17+
///
18+
/// ```rust
19+
/// #![forbid(warnings)]
20+
/// #![deny(bad_style)]
21+
///
22+
/// fn main() {}
23+
/// ```
24+
///
25+
/// {{produces}}
26+
///
27+
/// ### Recommended fix
28+
///
29+
/// If your crate is using `#![forbid(warnings)]`,
30+
/// we recommend that you change to `#![deny(warnings)]`.
31+
///
32+
/// ### Explanation
33+
///
34+
/// Due to a compiler bug, applying `forbid` to lint groups
35+
/// previously had no effect. The bug is now fixed but instead of
36+
/// enforcing `forbid` we issue this future-compatibility warning
37+
/// to avoid breaking existing crates.
38+
pub FORBIDDEN_LINT_GROUPS,
39+
Warn,
40+
"applying forbid to lint-groups",
41+
@future_incompatible = FutureIncompatibleInfo {
42+
reference: "issue #81670 <https://github.com/rust-lang/rust/issues/81670>",
43+
edition: None,
44+
};
45+
}
46+
1147
declare_lint! {
1248
/// The `ill_formed_attribute_input` lint detects ill-formed attribute
1349
/// inputs that were previously accepted and used in practice.
@@ -2839,6 +2875,7 @@ declare_lint_pass! {
28392875
/// Does nothing as a lint pass, but registers some `Lint`s
28402876
/// that are used by other parts of the compiler.
28412877
HardwiredLints => [
2878+
FORBIDDEN_LINT_GROUPS,
28422879
ILLEGAL_FLOATING_POINT_LITERAL_PATTERN,
28432880
ARITHMETIC_OVERFLOW,
28442881
UNCONDITIONAL_PANIC,

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

+11-3
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,17 @@ use rustc_data_structures::fx::FxHashMap;
55
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
66
use rustc_errors::{DiagnosticBuilder, DiagnosticId};
77
use rustc_hir::HirId;
8-
use rustc_session::lint::{builtin, Level, Lint, LintId};
8+
use rustc_session::lint::{
9+
builtin::{self, FORBIDDEN_LINT_GROUPS},
10+
Level, Lint, LintId,
11+
};
912
use rustc_session::{DiagnosticMessageId, Session};
1013
use rustc_span::hygiene::MacroKind;
1114
use rustc_span::source_map::{DesugaringKind, ExpnKind, MultiSpan};
1215
use rustc_span::{symbol, Span, Symbol, DUMMY_SP};
1316

1417
/// How a lint level was set.
15-
#[derive(Clone, Copy, PartialEq, Eq, HashStable)]
18+
#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)]
1619
pub enum LintLevelSource {
1720
/// Lint is at the default level as declared
1821
/// in rustc or a plugin.
@@ -87,7 +90,12 @@ impl LintLevelSets {
8790
// If we're about to issue a warning, check at the last minute for any
8891
// directives against the warnings "lint". If, for example, there's an
8992
// `allow(warnings)` in scope then we want to respect that instead.
90-
if level == Level::Warn {
93+
//
94+
// We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
95+
// triggers in cases (like #80988) where you have `forbid(warnings)`,
96+
// and so if we turned that into an error, it'd defeat the purpose of the
97+
// future compatibility warning.
98+
if level == Level::Warn && LintId::of(lint) != LintId::of(FORBIDDEN_LINT_GROUPS) {
9199
let (warnings_level, warnings_src) =
92100
self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux);
93101
if let Some(configured_warning_level) = warnings_level {

Diff for: src/test/ui/lint/forbid-group-group-1.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Check what happens when we forbid a smaller group but
2+
// then allow a superset of that group.
3+
4+
#![forbid(nonstandard_style)]
5+
6+
// FIXME: Arguably this should be an error, but the WARNINGS group is
7+
// treated in a very special (and rather ad-hoc) way and
8+
// it fails to trigger.
9+
#[allow(warnings)]
10+
fn main() {
11+
let A: ();
12+
//~^ ERROR should have a snake case name
13+
}

Diff for: src/test/ui/lint/forbid-group-group-1.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error: variable `A` should have a snake case name
2+
--> $DIR/forbid-group-group-1.rs:11:9
3+
|
4+
LL | let A: ();
5+
| ^ help: convert the identifier to snake case: `a`
6+
|
7+
note: the lint level is defined here
8+
--> $DIR/forbid-group-group-1.rs:4:11
9+
|
10+
LL | #![forbid(nonstandard_style)]
11+
| ^^^^^^^^^^^^^^^^^
12+
= note: `#[forbid(non_snake_case)]` implied by `#[forbid(nonstandard_style)]`
13+
14+
error: aborting due to previous error
15+

Diff for: src/test/ui/lint/forbid-group-group-2.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Check what happens when we forbid a bigger group but
2+
// then deny a subset of that group.
3+
4+
#![forbid(warnings)]
5+
#![deny(forbidden_lint_groups)]
6+
7+
#[allow(nonstandard_style)]
8+
//~^ ERROR incompatible with previous
9+
//~| WARNING previously accepted by the compiler
10+
//~| ERROR incompatible with previous
11+
//~| WARNING previously accepted by the compiler
12+
//~| ERROR incompatible with previous
13+
//~| WARNING previously accepted by the compiler
14+
//~| ERROR incompatible with previous
15+
//~| WARNING previously accepted by the compiler
16+
//~| ERROR incompatible with previous
17+
//~| WARNING previously accepted by the compiler
18+
//~| ERROR incompatible with previous
19+
//~| WARNING previously accepted by the compiler
20+
//~| ERROR incompatible with previous
21+
//~| WARNING previously accepted by the compiler
22+
//~| ERROR incompatible with previous
23+
//~| WARNING previously accepted by the compiler
24+
//~| ERROR incompatible with previous
25+
//~| WARNING previously accepted by the compiler
26+
fn main() {}

0 commit comments

Comments
 (0)