Skip to content

Commit

Permalink
Treat unstable lints as unknown
Browse files Browse the repository at this point in the history
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
David Koloski committed Mar 8, 2022
1 parent b97dc20 commit 8852752
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 35 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
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
92 changes: 57 additions & 35 deletions compiler/rustc_lint/src/levels.rs
Expand Up @@ -93,10 +93,21 @@ 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 +119,28 @@ 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,
) {
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 +163,7 @@ 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 +222,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 +248,11 @@ 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 +361,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 +382,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 @@ -378,7 +392,7 @@ 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);
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 +422,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 @@ -449,7 +463,7 @@ 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);
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), &sess);
struct_lint_level(
self.sess,
lint,
Expand All @@ -473,7 +487,7 @@ 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);
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 +518,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 +534,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 +545,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 +566,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 +589,25 @@ 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 (unknown_lints_level, _) = self.lint_level(builtin::UNKNOWN_LINTS);
if unknown_lints_level != Level::Allow {
feature_err(
&self.sess.parse_sess,
feature,
span,
&format!("the `{}` lint is unstable", lint_id.lint.name_lower()),
)
.emit();
}
return false;
}
}
true
}

/// Called after `push` when the scope of a set of attributes are exited.
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
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,11 @@ declare_lint! {
Warn,
"deprecated where clause location"
}

declare_lint! {
#[doc(hidden)]
pub TEST_UNSTABLE_LINT,
Deny,
"this unstable lint is only for testing",
@feature_gate = sym::test_unstable_lint;
}
1 change: 1 addition & 0 deletions compiler/rustc_span/src/symbol.rs
Expand Up @@ -1383,6 +1383,7 @@ symbols! {
test_case,
test_removed_feature,
test_runner,
test_unstable_lint,
then_with,
thread,
thread_local,
Expand Down
@@ -0,0 +1,4 @@
// check-pass
// compile-flags: -Aunknown_lints -Atest_unstable_lint

fn main() {}
@@ -0,0 +1,5 @@
// check-pass

#![allow(unknown_lints, test_unstable_lint)]

fn main() {}
@@ -0,0 +1,5 @@
// check-fail
// compile-flags: -Atest_unstable_lint
// error-pattern: the `test_unstable_lint` lint is unstable

fn main() {}
@@ -0,0 +1,11 @@
error[E0658]: the `test_unstable_lint` lint is unstable
|
= help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable

error[E0658]: the `test_unstable_lint` lint is unstable
|
= help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.
6 changes: 6 additions & 0 deletions src/test/ui/unknown-unstable-lints/unstable-lint-inline.rs
@@ -0,0 +1,6 @@
// check-fail
// error-pattern: the `test_unstable_lint` lint is unstable

#![allow(test_unstable_lint)]

fn main() {}
19 changes: 19 additions & 0 deletions src/test/ui/unknown-unstable-lints/unstable-lint-inline.stderr
@@ -0,0 +1,19 @@
error[E0658]: the `test_unstable_lint` lint is unstable
--> $DIR/unstable-lint-inline.rs:4:1
|
LL | #![allow(test_unstable_lint)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable

error[E0658]: the `test_unstable_lint` lint is unstable
--> $DIR/unstable-lint-inline.rs:4:1
|
LL | #![allow(test_unstable_lint)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: add `#![feature(test_unstable_lint)]` to the crate attributes to enable

error: aborting due to 2 previous errors

For more information about this error, try `rustc --explain E0658`.

0 comments on commit 8852752

Please sign in to comment.