diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index a69d28b184aed..09557332fd725 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -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), diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index bbfbf61f4869a..a72e0336db915 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -93,10 +93,21 @@ impl<'s> LintLevelsBuilder<'s> { self.store } + fn current_specs(&self) -> &FxHashMap { + &self.sets.list[self.cur].specs + } + + fn current_specs_mut(&mut self) -> &mut FxHashMap { + &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; @@ -108,17 +119,16 @@ 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 @@ -126,12 +136,11 @@ impl<'s> LintLevelsBuilder<'s> { /// diagnostic with no change to `specs`. fn insert_spec( &mut self, - specs: &mut FxHashMap, 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`. @@ -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| { @@ -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)); } } @@ -239,7 +248,11 @@ impl<'s> LintLevelsBuilder<'s> { is_crate_node: bool, source_hir_id: Option, ) -> 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() { @@ -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 @@ -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 @@ -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, @@ -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 @@ -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, @@ -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) @@ -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 @@ -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; } @@ -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, @@ -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 } @@ -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. diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 04a339f3c95a6..e1c88f74ab33c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3128,6 +3128,7 @@ declare_lint_pass! { SUSPICIOUS_AUTO_TRAIT_IMPLS, UNEXPECTED_CFGS, DEPRECATED_WHERE_CLAUSE_LOCATION, + TEST_UNSTABLE_LINT, ] } @@ -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; +} diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3f44292e03425..24f802c1d4f94 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1383,6 +1383,7 @@ symbols! { test_case, test_removed_feature, test_runner, + test_unstable_lint, then_with, thread, thread_local, diff --git a/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-command-line.rs b/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-command-line.rs new file mode 100644 index 0000000000000..80e30f23993e3 --- /dev/null +++ b/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-command-line.rs @@ -0,0 +1,4 @@ +// check-pass +// compile-flags: -Aunknown_lints -Atest_unstable_lint + +fn main() {} diff --git a/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-inline.rs b/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-inline.rs new file mode 100644 index 0000000000000..992472c894a8c --- /dev/null +++ b/src/test/ui/unknown-unstable-lints/allow-unknown-unstable-lint-inline.rs @@ -0,0 +1,5 @@ +// check-pass + +#![allow(unknown_lints, test_unstable_lint)] + +fn main() {} diff --git a/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.rs b/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.rs new file mode 100644 index 0000000000000..7663abffd9b48 --- /dev/null +++ b/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.rs @@ -0,0 +1,5 @@ +// check-fail +// compile-flags: -Atest_unstable_lint +// error-pattern: the `test_unstable_lint` lint is unstable + +fn main() {} diff --git a/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.stderr b/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.stderr new file mode 100644 index 0000000000000..d088f4c8fe5aa --- /dev/null +++ b/src/test/ui/unknown-unstable-lints/unstable-lint-command-line.stderr @@ -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`. diff --git a/src/test/ui/unknown-unstable-lints/unstable-lint-inline.rs b/src/test/ui/unknown-unstable-lints/unstable-lint-inline.rs new file mode 100644 index 0000000000000..43789906ed6f0 --- /dev/null +++ b/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() {} diff --git a/src/test/ui/unknown-unstable-lints/unstable-lint-inline.stderr b/src/test/ui/unknown-unstable-lints/unstable-lint-inline.stderr new file mode 100644 index 0000000000000..5ec85f346fec5 --- /dev/null +++ b/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`.