Skip to content

Commit

Permalink
Rollup merge of #94274 - djkoloski:unknown_unstable_lints, r=tmandry
Browse files Browse the repository at this point in the history
Treat unstable lints as unknown

This change causes unstable lints to be ignored if the `unknown_lints`
lint is allowed. To achieve this, it also changes lints to apply as soon
as they are processed. Previously, lints in the same set were processed
as a batch and then all simultaneously applied.

Implementation of rust-lang/compiler-team#469
  • Loading branch information
Dylan-DPC authored Mar 10, 2022
2 parents 5a7f09d + 1c31a95 commit 1ed2a94
Show file tree
Hide file tree
Showing 21 changed files with 383 additions and 114 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,8 @@ declare_features! (
(active, staged_api, "1.0.0", None, None),
/// Added for testing E0705; perma-unstable.
(active, test_2018_feature, "1.31.0", None, Some(Edition::Edition2018)),
/// Added for testing unstable lints; perma-unstable.
(active, test_unstable_lint, "1.60.0", None, None),
/// Allows non-`unsafe` —and thus, unsound— access to `Pin` constructions.
/// Marked `incomplete` since perma-unstable and unsound.
(incomplete, unsafe_pin_internals, "1.60.0", None, None),
Expand Down
116 changes: 72 additions & 44 deletions compiler/rustc_lint/src/levels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS},
Level, Lint, LintExpectationId, LintId,
};
use rustc_session::parse::feature_err;
use rustc_session::parse::{add_feature_diagnostics, feature_err};
use rustc_session::Session;
use rustc_span::symbol::{sym, Symbol};
use rustc_span::{source_map::MultiSpan, Span, DUMMY_SP};
Expand Down Expand Up @@ -93,10 +93,19 @@ impl<'s> LintLevelsBuilder<'s> {
self.store
}

fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
&self.sets.list[self.cur].specs
}

fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
&mut self.sets.list[self.cur].specs
}

fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
let mut specs = FxHashMap::default();
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid);

self.cur =
self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
for &(ref lint_name, level) in &sess.opts.lint_opts {
store.check_lint_name_cmdline(sess, &lint_name, level, self.registered_tools);
let orig_level = level;
Expand All @@ -108,30 +117,24 @@ impl<'s> LintLevelsBuilder<'s> {
};
for id in ids {
// ForceWarn and Forbid cannot be overriden
if let Some((Level::ForceWarn | Level::Forbid, _)) = specs.get(&id) {
if let Some((Level::ForceWarn | Level::Forbid, _)) = self.current_specs().get(&id) {
continue;
}

self.check_gated_lint(id, DUMMY_SP);
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
specs.insert(id, (level, src));
if self.check_gated_lint(id, DUMMY_SP) {
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level);
self.current_specs_mut().insert(id, (level, src));
}
}
}

self.cur = self.sets.list.push(LintSet { specs, parent: COMMAND_LINE });
}

/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
/// (e.g. if a forbid was already inserted on the same scope), then emits a
/// diagnostic with no change to `specs`.
fn insert_spec(
&mut self,
specs: &mut FxHashMap<LintId, LevelAndSource>,
id: LintId,
(level, src): LevelAndSource,
) {
fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) {
let (old_level, old_src) =
self.sets.get_lint_level(id.lint, self.cur, Some(&specs), &self.sess);
self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess);
// Setting to a non-forbid level is an error if the lint previously had
// a forbid level. Note that this is not necessarily true even with a
// `#[forbid(..)]` attribute present, as that is overriden by `--cap-lints`.
Expand All @@ -154,7 +157,10 @@ impl<'s> LintLevelsBuilder<'s> {
};
debug!(
"fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}",
fcw_warning, specs, old_src, id_name
fcw_warning,
self.current_specs(),
old_src,
id_name
);

let decorate_diag = |diag: &mut Diagnostic| {
Expand Down Expand Up @@ -213,9 +219,9 @@ impl<'s> LintLevelsBuilder<'s> {
}
}
if let Level::ForceWarn = old_level {
specs.insert(id, (old_level, old_src));
self.current_specs_mut().insert(id, (old_level, old_src));
} else {
specs.insert(id, (level, src));
self.current_specs_mut().insert(id, (level, src));
}
}

Expand All @@ -239,7 +245,9 @@ impl<'s> LintLevelsBuilder<'s> {
is_crate_node: bool,
source_hir_id: Option<HirId>,
) -> BuilderPush {
let mut specs = FxHashMap::default();
let prev = self.cur;
self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });

let sess = self.sess;
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
for (attr_index, attr) in attrs.iter().enumerate() {
Expand Down Expand Up @@ -348,8 +356,9 @@ impl<'s> LintLevelsBuilder<'s> {
reason,
);
for &id in *ids {
self.check_gated_lint(id, attr.span);
self.insert_spec(&mut specs, id, (level, src));
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
}
}
if let Level::Expect(expect_id) = level {
self.lint_expectations
Expand All @@ -368,7 +377,7 @@ impl<'s> LintLevelsBuilder<'s> {
reason,
);
for id in ids {
self.insert_spec(&mut specs, *id, (level, src));
self.insert_spec(*id, (level, src));
}
if let Level::Expect(expect_id) = level {
self.lint_expectations
Expand All @@ -377,8 +386,12 @@ impl<'s> LintLevelsBuilder<'s> {
}
Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let (lvl, src) = self.sets.get_lint_level(
lint,
self.cur,
Some(self.current_specs()),
&sess,
);
struct_lint_level(
self.sess,
lint,
Expand Down Expand Up @@ -408,7 +421,7 @@ impl<'s> LintLevelsBuilder<'s> {
reason,
);
for id in ids {
self.insert_spec(&mut specs, *id, (level, src));
self.insert_spec(*id, (level, src));
}
if let Level::Expect(expect_id) = level {
self.lint_expectations
Expand Down Expand Up @@ -448,8 +461,12 @@ impl<'s> LintLevelsBuilder<'s> {

CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (renamed_lint_level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess);
let (renamed_lint_level, src) = self.sets.get_lint_level(
lint,
self.cur,
Some(self.current_specs()),
&sess,
);
struct_lint_level(
self.sess,
lint,
Expand All @@ -472,8 +489,12 @@ impl<'s> LintLevelsBuilder<'s> {
}
CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS;
let (level, src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
let (level, src) = self.sets.get_lint_level(
lint,
self.cur,
Some(self.current_specs()),
self.sess,
);
struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name)
Expand Down Expand Up @@ -504,8 +525,9 @@ impl<'s> LintLevelsBuilder<'s> {
{
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason);
for &id in ids {
self.check_gated_lint(id, attr.span);
self.insert_spec(&mut specs, id, (level, src));
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
}
}
if let Level::Expect(expect_id) = level {
self.lint_expectations
Expand All @@ -519,7 +541,7 @@ impl<'s> LintLevelsBuilder<'s> {
}

if !is_crate_node {
for (id, &(level, ref src)) in specs.iter() {
for (id, &(level, ref src)) in self.current_specs().iter() {
if !id.lint.crate_level_only {
continue;
}
Expand All @@ -530,7 +552,7 @@ impl<'s> LintLevelsBuilder<'s> {

let lint = builtin::UNUSED_ATTRIBUTES;
let (lint_level, lint_src) =
self.sets.get_lint_level(lint, self.cur, Some(&specs), self.sess);
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess);
struct_lint_level(
self.sess,
lint,
Expand All @@ -551,9 +573,9 @@ impl<'s> LintLevelsBuilder<'s> {
}
}

let prev = self.cur;
if !specs.is_empty() {
self.cur = self.sets.list.push(LintSet { specs, parent: prev });
if self.current_specs().is_empty() {
self.sets.list.pop();
self.cur = prev;
}

BuilderPush { prev, changed: prev != self.cur }
Expand All @@ -574,18 +596,24 @@ impl<'s> LintLevelsBuilder<'s> {
}

/// Checks if the lint is gated on a feature that is not enabled.
fn check_gated_lint(&self, lint_id: LintId, span: Span) {
///
/// Returns `true` if the lint's feature is enabled.
fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
if let Some(feature) = lint_id.lint.feature_gate {
if !self.sess.features_untracked().enabled(feature) {
feature_err(
&self.sess.parse_sess,
feature,
span,
&format!("the `{}` lint is unstable", lint_id.lint.name_lower()),
)
.emit();
let lint = builtin::UNKNOWN_LINTS;
let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS);
struct_lint_level(self.sess, lint, level, src, Some(span.into()), |lint_db| {
let mut db =
lint_db.build(&format!("unknown lint: `{}`", lint_id.lint.name_lower()));
db.note(&format!("the `{}` lint is unstable", lint_id.lint.name_lower(),));
add_feature_diagnostics(&mut db, &self.sess.parse_sess, feature);
db.emit();
});
return false;
}
}
true
}

/// Called after `push` when the scope of a set of attributes are exited.
Expand Down
23 changes: 23 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3128,6 +3128,7 @@ declare_lint_pass! {
SUSPICIOUS_AUTO_TRAIT_IMPLS,
UNEXPECTED_CFGS,
DEPRECATED_WHERE_CLAUSE_LOCATION,
TEST_UNSTABLE_LINT,
]
}

Expand Down Expand Up @@ -3771,3 +3772,25 @@ declare_lint! {
Warn,
"deprecated where clause location"
}

declare_lint! {
/// The `test_unstable_lint` lint tests unstable lints and is perma-unstable.
///
/// ### Example
///
/// ```
/// #![allow(test_unstable_lint)]
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// In order to test the behavior of unstable lints, a permanently-unstable
/// lint is required. This lint can be used to trigger warnings and errors
/// from the compiler related to unstable lints.
pub TEST_UNSTABLE_LINT,
Deny,
"this unstable lint is only for testing",
@feature_gate = sym::test_unstable_lint;
}
21 changes: 19 additions & 2 deletions compiler/rustc_session/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,26 @@ pub fn feature_err_issue<'a>(
explain: &str,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let mut err = sess.span_diagnostic.struct_span_err_with_code(span, explain, error_code!(E0658));
add_feature_diagnostics_for_issue(&mut err, sess, feature, issue);
err
}

/// Adds the diagnostics for a feature to an existing error.
pub fn add_feature_diagnostics<'a>(err: &mut Diagnostic, sess: &'a ParseSess, feature: Symbol) {
add_feature_diagnostics_for_issue(err, sess, feature, GateIssue::Language);
}

/// Adds the diagnostics for a feature to an existing error.
///
/// This variant allows you to control whether it is a library or language feature.
/// Almost always, you want to use this for a language feature. If so, prefer
/// `add_feature_diagnostics`.
pub fn add_feature_diagnostics_for_issue<'a>(
err: &mut Diagnostic,
sess: &'a ParseSess,
feature: Symbol,
issue: GateIssue,
) {
if let Some(n) = find_feature_issue(feature, issue) {
err.note(&format!(
"see issue #{} <https://github.com/rust-lang/rust/issues/{}> for more information",
Expand All @@ -110,8 +129,6 @@ pub fn feature_err_issue<'a>(
if sess.unstable_features.is_nightly_build() {
err.help(&format!("add `#![feature({})]` to the crate attributes to enable", feature));
}

err
}

/// Info about a parsing session.
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,7 @@ symbols! {
test_case,
test_removed_feature,
test_runner,
test_unstable_lint,
then_with,
thread,
thread_local,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
// check-fail

#![deny(non_exhaustive_omitted_patterns)]
//~^ ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~^ WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
#![allow(non_exhaustive_omitted_patterns)]
//~^ ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~^ WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`

fn main() {
enum Foo {
A, B, C,
}

#[allow(non_exhaustive_omitted_patterns)]
//~^ WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
match Foo::A {
Foo::A => {}
Foo::B => {}
}
//~^^^^^ ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~^^^^ ERROR non-exhaustive patterns: `C` not covered

match Foo::A {
Foo::A => {}
Foo::B => {}
#[warn(non_exhaustive_omitted_patterns)]
_ => {}
}
//~^^^ ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~| ERROR the `non_exhaustive_omitted_patterns` lint is unstable
//~^^^ WARNING unknown lint: `non_exhaustive_omitted_patterns`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
}
Loading

0 comments on commit 1ed2a94

Please sign in to comment.