Skip to content
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

Treat unstable lints as unknown #94274

Merged
merged 5 commits into from
Mar 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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 @@ -1383,6 +1383,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`
}