-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
introduce future-compatibility warning for forbidden lint groups #81556
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,7 @@ use rustc_session::SessionLintStore; | |
use rustc_span::lev_distance::find_best_match_for_name; | ||
use rustc_span::{symbol::Symbol, MultiSpan, Span, DUMMY_SP}; | ||
use rustc_target::abi::LayoutOf; | ||
use tracing::debug; | ||
|
||
use std::cell::Cell; | ||
use std::slice; | ||
|
@@ -336,6 +337,20 @@ impl LintStore { | |
} | ||
} | ||
|
||
/// True if this symbol represents a lint group name. | ||
pub fn is_lint_group(&self, lint_name: Symbol) -> bool { | ||
debug!( | ||
"is_lint_group(lint_name={:?}, lint_groups={:?})", | ||
lint_name, | ||
self.lint_groups.keys().collect::<Vec<_>>() | ||
); | ||
let lint_name_str = &*lint_name.as_str(); | ||
self.lint_groups.contains_key(&lint_name_str) || { | ||
let warnings_name_str = crate::WARNINGS.name_lower(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For some reason, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm. Are there any other "obvious" groups that might also be not on that list for some reason? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No. The reason for WARNINGS is that it is mega hardcoded in the way that it's implemented; it's not a real lint group. |
||
lint_name_str == &*warnings_name_str | ||
} | ||
} | ||
|
||
/// Checks the name of a lint for its existence, and whether it was | ||
/// renamed or removed. Generates a DiagnosticBuilder containing a | ||
/// warning for renamed and removed lints. This is over both lint | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,7 +5,7 @@ use rustc_ast::attr; | |
use rustc_ast::unwrap_or; | ||
use rustc_ast_pretty::pprust; | ||
use rustc_data_structures::fx::FxHashMap; | ||
use rustc_errors::{struct_span_err, Applicability}; | ||
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; | ||
use rustc_hir as hir; | ||
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; | ||
use rustc_hir::{intravisit, HirId}; | ||
|
@@ -17,11 +17,15 @@ use rustc_middle::lint::{ | |
}; | ||
use rustc_middle::ty::query::Providers; | ||
use rustc_middle::ty::TyCtxt; | ||
use rustc_session::lint::{builtin, Level, Lint, LintId}; | ||
use rustc_session::lint::{ | ||
builtin::{self, FORBIDDEN_LINT_GROUPS}, | ||
Level, Lint, LintId, | ||
}; | ||
use rustc_session::parse::feature_err; | ||
use rustc_session::Session; | ||
use rustc_span::symbol::{sym, Symbol}; | ||
use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP}; | ||
use tracing::debug; | ||
|
||
use std::cmp; | ||
|
||
|
@@ -51,6 +55,7 @@ pub struct LintLevelsBuilder<'s> { | |
id_to_set: FxHashMap<HirId, u32>, | ||
cur: u32, | ||
warn_about_weird_lints: bool, | ||
store: &'s LintStore, | ||
} | ||
|
||
pub struct BuilderPush { | ||
|
@@ -59,13 +64,14 @@ pub struct BuilderPush { | |
} | ||
|
||
impl<'s> LintLevelsBuilder<'s> { | ||
pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &LintStore) -> Self { | ||
pub fn new(sess: &'s Session, warn_about_weird_lints: bool, store: &'s LintStore) -> Self { | ||
let mut builder = LintLevelsBuilder { | ||
sess, | ||
sets: LintLevelSets::new(), | ||
cur: 0, | ||
id_to_set: Default::default(), | ||
warn_about_weird_lints, | ||
store, | ||
}; | ||
builder.process_command_line(sess, store); | ||
assert_eq!(builder.sets.list.len(), 1); | ||
|
@@ -120,36 +126,75 @@ impl<'s> LintLevelsBuilder<'s> { | |
if let (Level::Forbid, old_src) = | ||
self.sets.get_lint_level(id.lint, self.cur, Some(&specs), &self.sess) | ||
{ | ||
let mut diag_builder = struct_span_err!( | ||
self.sess, | ||
src.span(), | ||
E0453, | ||
"{}({}) incompatible with previous forbid", | ||
level.as_str(), | ||
src.name(), | ||
// Backwards compatibility check: | ||
// | ||
// We used to not consider `forbid(lint_group)` | ||
// as preventing `allow(lint)` for some lint `lint` in | ||
// `lint_group`. For now, issue a future-compatibility | ||
// warning for this case. | ||
let id_name = id.lint.name_lower(); | ||
let fcw_warning = match old_src { | ||
LintLevelSource::Default => false, | ||
LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol), | ||
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), | ||
}; | ||
debug!( | ||
"fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", | ||
fcw_warning, specs, old_src, id_name | ||
); | ||
diag_builder.span_label(src.span(), "overruled by previous forbid"); | ||
match old_src { | ||
LintLevelSource::Default => { | ||
diag_builder.note(&format!( | ||
"`forbid` lint level is the default for {}", | ||
id.to_string() | ||
)); | ||
} | ||
LintLevelSource::Node(_, forbid_source_span, reason) => { | ||
diag_builder.span_label(forbid_source_span, "`forbid` level set here"); | ||
if let Some(rationale) = reason { | ||
diag_builder.note(&rationale.as_str()); | ||
|
||
let decorate_diag_builder = |mut diag_builder: DiagnosticBuilder<'_>| { | ||
diag_builder.span_label(src.span(), "overruled by previous forbid"); | ||
match old_src { | ||
LintLevelSource::Default => { | ||
diag_builder.note(&format!( | ||
"`forbid` lint level is the default for {}", | ||
id.to_string() | ||
)); | ||
} | ||
LintLevelSource::Node(_, forbid_source_span, reason) => { | ||
diag_builder.span_label(forbid_source_span, "`forbid` level set here"); | ||
if let Some(rationale) = reason { | ||
diag_builder.note(&rationale.as_str()); | ||
} | ||
} | ||
LintLevelSource::CommandLine(_, _) => { | ||
diag_builder.note("`forbid` lint level was set on command line"); | ||
} | ||
} | ||
LintLevelSource::CommandLine(_, _) => { | ||
diag_builder.note("`forbid` lint level was set on command line"); | ||
} | ||
diag_builder.emit(); | ||
}; | ||
if !fcw_warning { | ||
let diag_builder = struct_span_err!( | ||
self.sess, | ||
src.span(), | ||
E0453, | ||
"{}({}) incompatible with previous forbid", | ||
level.as_str(), | ||
src.name(), | ||
); | ||
decorate_diag_builder(diag_builder); | ||
} else { | ||
self.struct_lint( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The logic for creating lints and errors is setup in annoyingly non-analogous ways, but I resisted deeper refactorings. |
||
FORBIDDEN_LINT_GROUPS, | ||
Some(src.span().into()), | ||
|diag_builder| { | ||
let diag_builder = diag_builder.build(&format!( | ||
"{}({}) incompatible with previous forbid", | ||
level.as_str(), | ||
src.name(), | ||
)); | ||
decorate_diag_builder(diag_builder); | ||
}, | ||
); | ||
} | ||
diag_builder.emit(); | ||
|
||
// Retain the forbid lint level | ||
return; | ||
// Retain the forbid lint level, unless we are | ||
// issuing a FCW. In the FCW case, we want to | ||
// respect the new setting. | ||
if !fcw_warning { | ||
return; | ||
} | ||
} | ||
} | ||
specs.insert(id, (level, src)); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Check what happens when we forbid a smaller group but | ||
// then allow a superset of that group. | ||
|
||
#![forbid(nonstandard_style)] | ||
|
||
// FIXME: Arguably this should be an error, but the WARNINGS group is | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. does this mean you should have a different test that tries this subset/superset case using something other than the special cased WARNINGS group? (or is the WARNINGS group the only instance of a superset in the first place...?) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I couldn't find any other group to use. |
||
// treated in a very special (and rather ad-hoc) way and | ||
// it fails to trigger. | ||
#[allow(warnings)] | ||
fn main() { | ||
let A: (); | ||
//~^ ERROR should have a snake case name | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
error: variable `A` should have a snake case name | ||
--> $DIR/forbid-group-group-1.rs:11:9 | ||
| | ||
LL | let A: (); | ||
| ^ help: convert the identifier to snake case: `a` | ||
| | ||
note: the lint level is defined here | ||
--> $DIR/forbid-group-group-1.rs:4:11 | ||
| | ||
LL | #![forbid(nonstandard_style)] | ||
| ^^^^^^^^^^^^^^^^^ | ||
= note: `#[forbid(non_snake_case)]` implied by `#[forbid(nonstandard_style)]` | ||
|
||
error: aborting due to previous error | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Check what happens when we forbid a bigger group but | ||
// then deny a subset of that group. | ||
|
||
#![forbid(warnings)] | ||
#![deny(forbidden_lint_groups)] | ||
|
||
#[allow(nonstandard_style)] | ||
//~^ ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
//~| ERROR incompatible with previous | ||
//~| WARNING previously accepted by the compiler | ||
fn main() {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know why everything with lints uses as keys, but I couldn't find other ways to do comparisons.