Auto merge of #101620 - cjgillot:compute_lint_levels_by_def, r=oli-obk

Compute lint levels by definition

Lint levels are currently computed once for the whole crate. Any code that wants to emit a lint depends on this single `lint_levels(())` query. This query contains the `Span` for each attribute that participates in the lint level tree, so any code that wants to emit a lint basically depends on the spans in all files in the crate.

Contrary to hard errors, we do not clear the incremental session on lints, so this implicit world dependency pessimizes incremental reuse. (And is furthermore invisible for allowed lints.)

This PR completes https://github.com/rust-lang/rust/pull/99634 (thanks for the initial work `@fee1-dead)` and includes it in the dependency graph.

The design is based on 2 queries:
1. `lint_levels_on(HirId) -> FxHashMap<LintId, LevelAndSource>` which accesses the attributes at the given `HirId` and processes them into lint levels.  The `TyCtxt` is responsible for probing the HIR tree to find the user-visible level.
2. `lint_expectations(())` which lists all the `#[expect]` attributes in the crate.

This PR also introduces the ability to reconstruct a `HirId` from a `DepNode` by encoding the local part of the `DefPathHash` and the `ItemLocalId` in the two `u64` of the fingerprint.  This allows for the dep-graph to directly recompute `lint_levels_on` directly, without having to force the calling query.

Closes https://github.com/rust-lang/rust/issues/95094.
Supersedes https://github.com/rust-lang/rust/pull/99634.
This commit is contained in:
bors 2022-09-15 00:01:17 +00:00
commit 2cb9a65684
18 changed files with 684 additions and 502 deletions

View File

@ -338,9 +338,10 @@ impl Diagnostic {
// The lint index inside the attribute is manually transferred here. // The lint index inside the attribute is manually transferred here.
let lint_index = expectation_id.get_lint_index(); let lint_index = expectation_id.get_lint_index();
expectation_id.set_lint_index(None); expectation_id.set_lint_index(None);
let mut stable_id = *unstable_to_stable let mut stable_id = unstable_to_stable
.get(&expectation_id) .get(&expectation_id)
.expect("each unstable `LintExpectationId` must have a matching stable id"); .expect("each unstable `LintExpectationId` must have a matching stable id")
.normalize();
stable_id.set_lint_index(lint_index); stable_id.set_lint_index(lint_index);
*expectation_id = stable_id; *expectation_id = stable_id;

View File

@ -1167,7 +1167,7 @@ impl HandlerInner {
if let Some(expectation_id) = diagnostic.level.get_expectation_id() { if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
self.suppressed_expected_diag = true; self.suppressed_expected_diag = true;
self.fulfilled_expectations.insert(expectation_id); self.fulfilled_expectations.insert(expectation_id.normalize());
} }
if matches!(diagnostic.level, Warning(_)) if matches!(diagnostic.level, Warning(_))

View File

@ -560,7 +560,7 @@ pub struct LateContext<'tcx> {
/// Context for lint checking of the AST, after expansion, before lowering to HIR. /// Context for lint checking of the AST, after expansion, before lowering to HIR.
pub struct EarlyContext<'a> { pub struct EarlyContext<'a> {
pub builder: LintLevelsBuilder<'a>, pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
pub buffered: LintBuffer, pub buffered: LintBuffer,
} }

View File

@ -59,6 +59,7 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
F: FnOnce(&mut Self), F: FnOnce(&mut Self),
{ {
let is_crate_node = id == ast::CRATE_NODE_ID; let is_crate_node = id == ast::CRATE_NODE_ID;
debug!(?id);
let push = self.context.builder.push(attrs, is_crate_node, None); let push = self.context.builder.push(attrs, is_crate_node, None);
self.check_id(id); self.check_id(id);

View File

@ -16,8 +16,10 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
return; return;
} }
let lint_expectations = tcx.lint_expectations(());
let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids(); let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids();
let lint_expectations = &tcx.lint_levels(()).lint_expectations;
tracing::debug!(?lint_expectations, ?fulfilled_expectations);
for (id, expectation) in lint_expectations { for (id, expectation) in lint_expectations {
// This check will always be true, since `lint_expectations` only // This check will always be true, since `lint_expectations` only

View File

@ -6,10 +6,11 @@ use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan}; use rustc_errors::{Applicability, Diagnostic, LintDiagnosticBuilder, MultiSpan};
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::{intravisit, HirId}; use rustc_hir::{intravisit, HirId};
use rustc_index::vec::IndexVec;
use rustc_middle::hir::nested_filter; use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{ use rustc_middle::lint::{
struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets, reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE, ShallowLintLevelMap,
}; };
use rustc_middle::ty::query::Providers; use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_middle::ty::{RegisteredTools, TyCtxt};
@ -27,47 +28,292 @@ use crate::errors::{
UnknownToolInScopedLint, UnknownToolInScopedLint,
}; };
fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { /// Collection of lint levels for the whole crate.
let store = unerased_lint_store(tcx); /// This is used by AST-based lints, which do not
let levels = /// wait until we have built HIR to be emitted.
LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools); #[derive(Debug)]
let mut builder = LintLevelMapBuilder { levels, tcx }; struct LintLevelSets {
let krate = tcx.hir().krate(); /// Linked list of specifications.
list: IndexVec<LintStackIndex, LintSet>,
builder.levels.id_to_set.reserve(krate.owners.len() + 1);
let push =
builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true, Some(hir::CRATE_HIR_ID));
builder.levels.register_id(hir::CRATE_HIR_ID);
tcx.hir().walk_toplevel_module(&mut builder);
builder.levels.pop(push);
builder.levels.update_unstable_expectation_ids();
builder.levels.build_map()
} }
pub struct LintLevelsBuilder<'s> { rustc_index::newtype_index! {
sess: &'s Session, struct LintStackIndex {
lint_expectations: Vec<(LintExpectationId, LintExpectation)>, ENCODABLE = custom, // we don't need encoding
/// Each expectation has a stable and an unstable identifier. This map const COMMAND_LINE = 0,
/// is used to map from unstable to stable [`LintExpectationId`]s. }
expectation_id_map: FxHashMap<LintExpectationId, LintExpectationId>, }
/// Specifications found at this position in the stack. This map only represents the lints
/// found for one set of attributes (like `shallow_lint_levels_on` does).
///
/// We store the level specifications as a linked list.
/// Each `LintSet` represents a set of attributes on the same AST node.
/// The `parent` forms a linked list that matches the AST tree.
/// This way, walking the linked list is equivalent to walking the AST bottom-up
/// to find the specifications for a given lint.
#[derive(Debug)]
struct LintSet {
// -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
// flag.
specs: FxHashMap<LintId, LevelAndSource>,
parent: LintStackIndex,
}
impl LintLevelSets {
fn new() -> Self {
LintLevelSets { list: IndexVec::new() }
}
fn get_lint_level(
&self,
lint: &'static Lint,
idx: LintStackIndex,
aux: Option<&FxHashMap<LintId, LevelAndSource>>,
sess: &Session,
) -> LevelAndSource {
let lint = LintId::of(lint);
let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
let level = reveal_actual_level(level, &mut src, sess, lint, |id| {
self.raw_lint_id_level(id, idx, aux)
});
(level, src)
}
fn raw_lint_id_level(
&self,
id: LintId,
mut idx: LintStackIndex,
aux: Option<&FxHashMap<LintId, LevelAndSource>>,
) -> (Option<Level>, LintLevelSource) {
if let Some(specs) = aux {
if let Some(&(level, src)) = specs.get(&id) {
return (Some(level), src);
}
}
loop {
let LintSet { ref specs, parent } = self.list[idx];
if let Some(&(level, src)) = specs.get(&id) {
return (Some(level), src);
}
if idx == COMMAND_LINE {
return (None, LintLevelSource::Default);
}
idx = parent;
}
}
}
fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
let store = unerased_lint_store(tcx);
let mut builder = LintLevelsBuilder {
sess: tcx.sess,
provider: QueryMapExpectationsWrapper {
tcx,
cur: hir::CRATE_HIR_ID,
specs: ShallowLintLevelMap::default(),
expectations: Vec::new(),
unstable_to_stable_ids: FxHashMap::default(),
},
warn_about_weird_lints: false,
store,
registered_tools: &tcx.resolutions(()).registered_tools,
};
builder.add_command_line();
builder.add_id(hir::CRATE_HIR_ID);
tcx.hir().walk_toplevel_module(&mut builder);
tcx.sess.diagnostic().update_unstable_expectation_id(&builder.provider.unstable_to_stable_ids);
builder.provider.expectations
}
fn shallow_lint_levels_on(tcx: TyCtxt<'_>, hir_id: HirId) -> ShallowLintLevelMap {
let store = unerased_lint_store(tcx);
let mut levels = LintLevelsBuilder {
sess: tcx.sess,
provider: LintLevelQueryMap { tcx, cur: hir_id, specs: ShallowLintLevelMap::default() },
warn_about_weird_lints: false,
store,
registered_tools: &tcx.resolutions(()).registered_tools,
};
let is_crate = hir::CRATE_HIR_ID == hir_id;
if is_crate {
levels.add_command_line();
}
debug!(?hir_id);
levels.add(tcx.hir().attrs(hir_id), is_crate, Some(hir_id));
levels.provider.specs
}
pub struct TopDown {
sets: LintLevelSets, sets: LintLevelSets,
id_to_set: FxHashMap<HirId, LintStackIndex>,
cur: LintStackIndex, cur: LintStackIndex,
}
pub trait LintLevelsProvider {
fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource>;
fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource>;
fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource;
fn push_expectation(&mut self, _id: LintExpectationId, _expectation: LintExpectation) {}
}
impl LintLevelsProvider for TopDown {
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 get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource {
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess)
}
}
struct LintLevelQueryMap<'tcx> {
tcx: TyCtxt<'tcx>,
cur: HirId,
specs: ShallowLintLevelMap,
}
impl LintLevelsProvider for LintLevelQueryMap<'_> {
fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
&self.specs.specs
}
fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
&mut self.specs.specs
}
fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
}
}
struct QueryMapExpectationsWrapper<'tcx> {
tcx: TyCtxt<'tcx>,
cur: HirId,
specs: ShallowLintLevelMap,
expectations: Vec<(LintExpectationId, LintExpectation)>,
unstable_to_stable_ids: FxHashMap<LintExpectationId, LintExpectationId>,
}
impl LintLevelsProvider for QueryMapExpectationsWrapper<'_> {
fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
&self.specs.specs
}
fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
self.specs.specs.clear();
&mut self.specs.specs
}
fn get_lint_level(&self, lint: &'static Lint, _: &Session) -> LevelAndSource {
self.specs.lint_level_id_at_node(self.tcx, LintId::of(lint), self.cur)
}
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
let LintExpectationId::Stable { attr_id: Some(attr_id), hir_id, attr_index, .. } = id else { bug!("unstable expectation id should already be mapped") };
let key = LintExpectationId::Unstable { attr_id, lint_index: None };
if !self.unstable_to_stable_ids.contains_key(&key) {
self.unstable_to_stable_ids.insert(
key,
LintExpectationId::Stable { hir_id, attr_index, lint_index: None, attr_id: None },
);
}
self.expectations.push((id.normalize(), expectation));
}
}
impl<'tcx> LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
fn add_id(&mut self, hir_id: HirId) {
self.add(self.provider.tcx.hir().attrs(hir_id), hir_id == hir::CRATE_HIR_ID, Some(hir_id));
}
}
impl<'tcx> intravisit::Visitor<'tcx> for LintLevelsBuilder<'_, QueryMapExpectationsWrapper<'tcx>> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.provider.tcx.hir()
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
self.add_id(param.hir_id);
intravisit::walk_param(self, param);
}
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
self.add_id(it.hir_id());
intravisit::walk_item(self, it);
}
fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
self.add_id(it.hir_id());
intravisit::walk_foreign_item(self, it);
}
fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
// We will call `add_id` when we walk
// the `StmtKind`. The outer statement itself doesn't
// define the lint levels.
intravisit::walk_stmt(self, e);
}
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
self.add_id(e.hir_id);
intravisit::walk_expr(self, e);
}
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
self.add_id(s.hir_id);
intravisit::walk_field_def(self, s);
}
fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
self.add_id(v.id);
intravisit::walk_variant(self, v);
}
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
self.add_id(l.hir_id);
intravisit::walk_local(self, l);
}
fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
self.add_id(a.hir_id);
intravisit::walk_arm(self, a);
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
self.add_id(trait_item.hir_id());
intravisit::walk_trait_item(self, trait_item);
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
self.add_id(impl_item.hir_id());
intravisit::walk_impl_item(self, impl_item);
}
}
pub struct LintLevelsBuilder<'s, P> {
sess: &'s Session,
provider: P,
warn_about_weird_lints: bool, warn_about_weird_lints: bool,
store: &'s LintStore, store: &'s LintStore,
registered_tools: &'s RegisteredTools, registered_tools: &'s RegisteredTools,
} }
pub struct BuilderPush { pub(crate) struct BuilderPush {
prev: LintStackIndex, prev: LintStackIndex,
pub changed: bool,
} }
impl<'s> LintLevelsBuilder<'s> { impl<'s> LintLevelsBuilder<'s, TopDown> {
pub fn new( pub(crate) fn new(
sess: &'s Session, sess: &'s Session,
warn_about_weird_lints: bool, warn_about_weird_lints: bool,
store: &'s LintStore, store: &'s LintStore,
@ -75,20 +321,66 @@ impl<'s> LintLevelsBuilder<'s> {
) -> Self { ) -> Self {
let mut builder = LintLevelsBuilder { let mut builder = LintLevelsBuilder {
sess, sess,
lint_expectations: Default::default(), provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
expectation_id_map: Default::default(),
sets: LintLevelSets::new(),
cur: COMMAND_LINE,
id_to_set: Default::default(),
warn_about_weird_lints, warn_about_weird_lints,
store, store,
registered_tools, registered_tools,
}; };
builder.process_command_line(sess, store); builder.process_command_line();
assert_eq!(builder.sets.list.len(), 1); assert_eq!(builder.provider.sets.list.len(), 1);
builder builder
} }
fn process_command_line(&mut self) {
self.provider.cur = self
.provider
.sets
.list
.push(LintSet { specs: FxHashMap::default(), parent: COMMAND_LINE });
self.add_command_line();
}
/// Pushes a list of AST lint attributes onto this context.
///
/// This function will return a `BuilderPush` object which should be passed
/// to `pop` when this scope for the attributes provided is exited.
///
/// This function will perform a number of tasks:
///
/// * It'll validate all lint-related attributes in `attrs`
/// * It'll mark all lint-related attributes as used
/// * Lint levels will be updated based on the attributes provided
/// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
/// `#[allow]`
///
/// Don't forget to call `pop`!
pub(crate) fn push(
&mut self,
attrs: &[ast::Attribute],
is_crate_node: bool,
source_hir_id: Option<HirId>,
) -> BuilderPush {
let prev = self.provider.cur;
self.provider.cur =
self.provider.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
self.add(attrs, is_crate_node, source_hir_id);
if self.provider.current_specs().is_empty() {
self.provider.sets.list.pop();
self.provider.cur = prev;
}
BuilderPush { prev }
}
/// Called after `push` when the scope of a set of attributes are exited.
pub(crate) fn pop(&mut self, push: BuilderPush) {
self.provider.cur = push.prev;
}
}
impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
pub(crate) fn sess(&self) -> &Session { pub(crate) fn sess(&self) -> &Session {
self.sess self.sess
} }
@ -98,24 +390,20 @@ impl<'s> LintLevelsBuilder<'s> {
} }
fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> { fn current_specs(&self) -> &FxHashMap<LintId, LevelAndSource> {
&self.sets.list[self.cur].specs self.provider.current_specs()
} }
fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> { fn current_specs_mut(&mut self) -> &mut FxHashMap<LintId, LevelAndSource> {
&mut self.sets.list[self.cur].specs self.provider.current_specs_mut()
} }
fn process_command_line(&mut self, sess: &Session, store: &LintStore) { fn add_command_line(&mut self) {
self.sets.lint_cap = sess.opts.lint_cap.unwrap_or(Level::Forbid); for &(ref lint_name, level) in &self.sess.opts.lint_opts {
self.store.check_lint_name_cmdline(self.sess, &lint_name, level, self.registered_tools);
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; let orig_level = level;
let lint_flag_val = Symbol::intern(lint_name); let lint_flag_val = Symbol::intern(lint_name);
let Ok(ids) = store.find_lints(&lint_name) else { let Ok(ids) = self.store.find_lints(&lint_name) else {
// errors handled in check_lint_name_cmdline above // errors handled in check_lint_name_cmdline above
continue continue
}; };
@ -138,9 +426,11 @@ impl<'s> LintLevelsBuilder<'s> {
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful /// 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 /// (e.g. if a forbid was already inserted on the same scope), then emits a
/// diagnostic with no change to `specs`. /// diagnostic with no change to `specs`.
fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) { fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
let (old_level, old_src) = let (old_level, old_src) = self.provider.get_lint_level(id.lint, &self.sess);
self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess); if let Level::Expect(id) = &mut level && let LintExpectationId::Stable { .. } = id {
*id = id.normalize();
}
// Setting to a non-forbid level is an error if the lint previously had // 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 // a forbid level. Note that this is not necessarily true even with a
// `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`. // `#[forbid(..)]` attribute present, as that is overridden by `--cap-lints`.
@ -158,7 +448,7 @@ impl<'s> LintLevelsBuilder<'s> {
let id_name = id.lint.name_lower(); let id_name = id.lint.name_lower();
let fcw_warning = match old_src { let fcw_warning = match old_src {
LintLevelSource::Default => false, LintLevelSource::Default => false,
LintLevelSource::Node(symbol, _, _) => self.store.is_lint_group(symbol), LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol), LintLevelSource::CommandLine(symbol, _) => self.store.is_lint_group(symbol),
}; };
debug!( debug!(
@ -178,8 +468,8 @@ impl<'s> LintLevelsBuilder<'s> {
id.to_string() id.to_string()
)); ));
} }
LintLevelSource::Node(_, forbid_source_span, reason) => { LintLevelSource::Node { span, reason, .. } => {
diag.span_label(forbid_source_span, "`forbid` level set here"); diag.span_label(span, "`forbid` level set here");
if let Some(rationale) = reason { if let Some(rationale) = reason {
diag.note(rationale.as_str()); diag.note(rationale.as_str());
} }
@ -199,11 +489,8 @@ impl<'s> LintLevelsBuilder<'s> {
LintLevelSource::Default => { LintLevelSource::Default => {
OverruledAttributeSub::DefaultSource { id: id.to_string() } OverruledAttributeSub::DefaultSource { id: id.to_string() }
} }
LintLevelSource::Node(_, forbid_source_span, reason) => { LintLevelSource::Node { span, reason, .. } => {
OverruledAttributeSub::NodeSource { OverruledAttributeSub::NodeSource { span, reason }
span: forbid_source_span,
reason,
}
} }
LintLevelSource::CommandLine(_, _) => { LintLevelSource::CommandLine(_, _) => {
OverruledAttributeSub::CommandLineSource OverruledAttributeSub::CommandLineSource
@ -256,29 +543,7 @@ impl<'s> LintLevelsBuilder<'s> {
}; };
} }
/// Pushes a list of AST lint attributes onto this context. fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
///
/// This function will return a `BuilderPush` object which should be passed
/// to `pop` when this scope for the attributes provided is exited.
///
/// This function will perform a number of tasks:
///
/// * It'll validate all lint-related attributes in `attrs`
/// * It'll mark all lint-related attributes as used
/// * Lint levels will be updated based on the attributes provided
/// * Lint attributes are validated, e.g., a `#[forbid]` can't be switched to
/// `#[allow]`
///
/// Don't forget to call `pop`!
pub(crate) fn push(
&mut self,
attrs: &[ast::Attribute],
is_crate_node: bool,
source_hir_id: Option<HirId>,
) -> BuilderPush {
let prev = self.cur;
self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
let sess = self.sess; let sess = self.sess;
for (attr_index, attr) in attrs.iter().enumerate() { for (attr_index, attr) in attrs.iter().enumerate() {
if attr.has_name(sym::automatically_derived) { if attr.has_name(sym::automatically_derived) {
@ -293,7 +558,17 @@ impl<'s> LintLevelsBuilder<'s> {
None => continue, None => continue,
// This is the only lint level with a `LintExpectationId` that can be created from an attribute // This is the only lint level with a `LintExpectationId` that can be created from an attribute
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => {
let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index); let LintExpectationId::Unstable { attr_id, lint_index } = unstable_id
else { bug!("stable id Level::from_attr") };
let stable_id = LintExpectationId::Stable {
hir_id,
attr_index: attr_index.try_into().unwrap(),
lint_index,
// we pass the previous unstable attr_id such that we can trace the ast id when building a map
// to go from unstable to stable id.
attr_id: Some(attr_id),
};
Level::Expect(stable_id) Level::Expect(stable_id)
} }
@ -408,7 +683,7 @@ impl<'s> LintLevelsBuilder<'s> {
[lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS), [lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
_ => false, _ => false,
}; };
self.lint_expectations.push(( self.provider.push_expectation(
expect_id, expect_id,
LintExpectation::new( LintExpectation::new(
reason, reason,
@ -416,13 +691,19 @@ impl<'s> LintLevelsBuilder<'s> {
is_unfulfilled_lint_expectations, is_unfulfilled_lint_expectations,
tool_name, tool_name,
), ),
)); );
} }
let src = LintLevelSource::Node( let src = LintLevelSource::Node {
meta_item.path.segments.last().expect("empty lint name").ident.name, name: meta_item
sp, .path
.segments
.last()
.expect("empty lint name")
.ident
.name,
span: sp,
reason, reason,
); };
for &id in *ids { for &id in *ids {
if self.check_gated_lint(id, attr.span) { if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src)); self.insert_spec(id, (level, src));
@ -435,31 +716,26 @@ impl<'s> LintLevelsBuilder<'s> {
Ok(ids) => { Ok(ids) => {
let complete_name = let complete_name =
&format!("{}::{}", tool_ident.unwrap().name, name); &format!("{}::{}", tool_ident.unwrap().name, name);
let src = LintLevelSource::Node( let src = LintLevelSource::Node {
Symbol::intern(complete_name), name: Symbol::intern(complete_name),
sp, span: sp,
reason, reason,
); };
for &id in ids { for &id in ids {
if self.check_gated_lint(id, attr.span) { if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src)); self.insert_spec(id, (level, src));
} }
} }
if let Level::Expect(expect_id) = level { if let Level::Expect(expect_id) = level {
self.lint_expectations.push(( self.provider.push_expectation(
expect_id, expect_id,
LintExpectation::new(reason, sp, false, tool_name), LintExpectation::new(reason, sp, false, tool_name),
)); );
} }
} }
Err((Some(ids), ref new_lint_name)) => { Err((Some(ids), ref new_lint_name)) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (lvl, src) = self.sets.get_lint_level( let (lvl, src) = self.provider.get_lint_level(lint, &sess);
lint,
self.cur,
Some(self.current_specs()),
&sess,
);
struct_lint_level( struct_lint_level(
self.sess, self.sess,
lint, lint,
@ -483,19 +759,19 @@ impl<'s> LintLevelsBuilder<'s> {
}, },
); );
let src = LintLevelSource::Node( let src = LintLevelSource::Node {
Symbol::intern(&new_lint_name), name: Symbol::intern(&new_lint_name),
sp, span: sp,
reason, reason,
); };
for id in ids { for id in ids {
self.insert_spec(*id, (level, src)); self.insert_spec(*id, (level, src));
} }
if let Level::Expect(expect_id) = level { if let Level::Expect(expect_id) = level {
self.lint_expectations.push(( self.provider.push_expectation(
expect_id, expect_id,
LintExpectation::new(reason, sp, false, tool_name), LintExpectation::new(reason, sp, false, tool_name),
)); );
} }
} }
Err((None, _)) => { Err((None, _)) => {
@ -521,12 +797,7 @@ impl<'s> LintLevelsBuilder<'s> {
CheckLintNameResult::Warning(msg, renamed) => { CheckLintNameResult::Warning(msg, renamed) => {
let lint = builtin::RENAMED_AND_REMOVED_LINTS; let lint = builtin::RENAMED_AND_REMOVED_LINTS;
let (renamed_lint_level, src) = self.sets.get_lint_level( let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess);
lint,
self.cur,
Some(self.current_specs()),
&sess,
);
struct_lint_level( struct_lint_level(
self.sess, self.sess,
lint, lint,
@ -549,12 +820,7 @@ impl<'s> LintLevelsBuilder<'s> {
} }
CheckLintNameResult::NoLint(suggestion) => { CheckLintNameResult::NoLint(suggestion) => {
let lint = builtin::UNKNOWN_LINTS; let lint = builtin::UNKNOWN_LINTS;
let (level, src) = self.sets.get_lint_level( let (level, src) = self.provider.get_lint_level(lint, self.sess);
lint,
self.cur,
Some(self.current_specs()),
self.sess,
);
struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| { struct_lint_level(self.sess, lint, level, src, Some(sp.into()), |lint| {
let name = if let Some(tool_ident) = tool_ident { let name = if let Some(tool_ident) = tool_ident {
format!("{}::{}", tool_ident.name, name) format!("{}::{}", tool_ident.name, name)
@ -583,17 +849,21 @@ impl<'s> LintLevelsBuilder<'s> {
if let CheckLintNameResult::Ok(ids) = if let CheckLintNameResult::Ok(ids) =
self.store.check_lint_name(&new_name, None, self.registered_tools) self.store.check_lint_name(&new_name, None, self.registered_tools)
{ {
let src = LintLevelSource::Node(Symbol::intern(&new_name), sp, reason); let src = LintLevelSource::Node {
name: Symbol::intern(&new_name),
span: sp,
reason,
};
for &id in ids { for &id in ids {
if self.check_gated_lint(id, attr.span) { if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src)); self.insert_spec(id, (level, src));
} }
} }
if let Level::Expect(expect_id) = level { if let Level::Expect(expect_id) = level {
self.lint_expectations.push(( self.provider.push_expectation(
expect_id, expect_id,
LintExpectation::new(reason, sp, false, tool_name), LintExpectation::new(reason, sp, false, tool_name),
)); );
} }
} else { } else {
panic!("renamed lint does not exist: {}", new_name); panic!("renamed lint does not exist: {}", new_name);
@ -608,13 +878,12 @@ impl<'s> LintLevelsBuilder<'s> {
continue; continue;
} }
let LintLevelSource::Node(lint_attr_name, lint_attr_span, _) = *src else { let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src else {
continue continue
}; };
let lint = builtin::UNUSED_ATTRIBUTES; let lint = builtin::UNUSED_ATTRIBUTES;
let (lint_level, lint_src) = let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess);
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess);
struct_lint_level( struct_lint_level(
self.sess, self.sess,
lint, lint,
@ -634,32 +903,13 @@ impl<'s> LintLevelsBuilder<'s> {
break; break;
} }
} }
if self.current_specs().is_empty() {
self.sets.list.pop();
self.cur = prev;
}
BuilderPush { prev, changed: prev != self.cur }
}
fn create_stable_id(
&mut self,
unstable_id: LintExpectationId,
hir_id: HirId,
attr_index: usize,
) -> LintExpectationId {
let stable_id =
LintExpectationId::Stable { hir_id, attr_index: attr_index as u16, lint_index: None };
self.expectation_id_map.insert(unstable_id, stable_id);
stable_id
} }
/// Checks if the lint is gated on a feature that is not enabled. /// Checks if the lint is gated on a feature that is not enabled.
/// ///
/// Returns `true` if the lint's feature is enabled. /// Returns `true` if the lint's feature is enabled.
// FIXME only emit this once for each attribute, instead of repeating it 4 times for
// pre-expansion lints, post-expansion lints, `shallow_lint_levels_on` and `lint_expectations`.
fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool { fn check_gated_lint(&self, lint_id: LintId, span: Span) -> bool {
if let Some(feature) = lint_id.lint.feature_gate { if let Some(feature) = lint_id.lint.feature_gate {
if !self.sess.features_untracked().enabled(feature) { if !self.sess.features_untracked().enabled(feature) {
@ -678,19 +928,14 @@ impl<'s> LintLevelsBuilder<'s> {
true true
} }
/// Called after `push` when the scope of a set of attributes are exited.
pub fn pop(&mut self, push: BuilderPush) {
self.cur = push.prev;
}
/// Find the lint level for a lint. /// Find the lint level for a lint.
pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) { pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.sets.get_lint_level(lint, self.cur, None, self.sess) self.provider.get_lint_level(lint, self.sess)
} }
/// Used to emit a lint-related diagnostic based on the current state of /// Used to emit a lint-related diagnostic based on the current state of
/// this lint context. /// this lint context.
pub fn struct_lint( pub(crate) fn struct_lint(
&self, &self,
lint: &'static Lint, lint: &'static Lint,
span: Option<MultiSpan>, span: Option<MultiSpan>,
@ -699,141 +944,8 @@ impl<'s> LintLevelsBuilder<'s> {
let (level, src) = self.lint_level(lint); let (level, src) = self.lint_level(lint);
struct_lint_level(self.sess, lint, level, src, span, decorate) struct_lint_level(self.sess, lint, level, src, span, decorate)
} }
/// Registers the ID provided with the current set of lints stored in
/// this context.
pub fn register_id(&mut self, id: HirId) {
self.id_to_set.insert(id, self.cur);
}
fn update_unstable_expectation_ids(&self) {
self.sess.diagnostic().update_unstable_expectation_id(&self.expectation_id_map);
}
pub fn build_map(self) -> LintLevelMap {
LintLevelMap {
sets: self.sets,
id_to_set: self.id_to_set,
lint_expectations: self.lint_expectations,
}
}
} }
struct LintLevelMapBuilder<'tcx> { pub(crate) fn provide(providers: &mut Providers) {
levels: LintLevelsBuilder<'tcx>, *providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
tcx: TyCtxt<'tcx>,
}
impl LintLevelMapBuilder<'_> {
fn with_lint_attrs<F>(&mut self, id: hir::HirId, f: F)
where
F: FnOnce(&mut Self),
{
let is_crate_hir = id == hir::CRATE_HIR_ID;
let attrs = self.tcx.hir().attrs(id);
let push = self.levels.push(attrs, is_crate_hir, Some(id));
if push.changed {
self.levels.register_id(id);
}
f(self);
self.levels.pop(push);
}
}
impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
self.with_lint_attrs(param.hir_id, |builder| {
intravisit::walk_param(builder, param);
});
}
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
self.with_lint_attrs(it.hir_id(), |builder| {
intravisit::walk_item(builder, it);
});
}
fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) {
self.with_lint_attrs(it.hir_id(), |builder| {
intravisit::walk_foreign_item(builder, it);
})
}
fn visit_stmt(&mut self, e: &'tcx hir::Stmt<'tcx>) {
// We will call `with_lint_attrs` when we walk
// the `StmtKind`. The outer statement itself doesn't
// define the lint levels.
intravisit::walk_stmt(self, e);
}
fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
self.with_lint_attrs(e.hir_id, |builder| {
intravisit::walk_expr(builder, e);
})
}
fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) {
self.with_lint_attrs(field.hir_id, |builder| {
intravisit::walk_expr_field(builder, field);
})
}
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
self.with_lint_attrs(s.hir_id, |builder| {
intravisit::walk_field_def(builder, s);
})
}
fn visit_variant(&mut self, v: &'tcx hir::Variant<'tcx>) {
self.with_lint_attrs(v.id, |builder| {
intravisit::walk_variant(builder, v);
})
}
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
self.with_lint_attrs(l.hir_id, |builder| {
intravisit::walk_local(builder, l);
})
}
fn visit_arm(&mut self, a: &'tcx hir::Arm<'tcx>) {
self.with_lint_attrs(a.hir_id, |builder| {
intravisit::walk_arm(builder, a);
})
}
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
self.with_lint_attrs(trait_item.hir_id(), |builder| {
intravisit::walk_trait_item(builder, trait_item);
});
}
fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
self.with_lint_attrs(impl_item.hir_id(), |builder| {
intravisit::walk_impl_item(builder, impl_item);
});
}
fn visit_pat_field(&mut self, field: &'tcx hir::PatField<'tcx>) {
self.with_lint_attrs(field.hir_id, |builder| {
intravisit::walk_pat_field(builder, field);
})
}
fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) {
self.with_lint_attrs(p.hir_id, |builder| {
intravisit::walk_generic_param(builder, p);
});
}
}
pub fn provide(providers: &mut Providers) {
providers.lint_levels = lint_levels;
} }

View File

@ -35,6 +35,7 @@
#![feature(iter_order_by)] #![feature(iter_order_by)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(let_else)] #![feature(let_else)]
#![feature(min_specialization)]
#![feature(never_type)] #![feature(never_type)]
#![recursion_limit = "256"] #![recursion_limit = "256"]

View File

@ -92,7 +92,7 @@ pub enum LintExpectationId {
/// stable and can be cached. The additional index ensures that nodes with /// stable and can be cached. The additional index ensures that nodes with
/// several expectations can correctly match diagnostics to the individual /// several expectations can correctly match diagnostics to the individual
/// expectation. /// expectation.
Stable { hir_id: HirId, attr_index: u16, lint_index: Option<u16> }, Stable { hir_id: HirId, attr_index: u16, lint_index: Option<u16>, attr_id: Option<AttrId> },
} }
impl LintExpectationId { impl LintExpectationId {
@ -116,13 +116,31 @@ impl LintExpectationId {
*lint_index = new_lint_index *lint_index = new_lint_index
} }
/// Prepares the id for hashing. Removes references to the ast.
/// Should only be called when the id is stable.
pub fn normalize(self) -> Self {
match self {
Self::Stable { hir_id, attr_index, lint_index, .. } => {
Self::Stable { hir_id, attr_index, lint_index, attr_id: None }
}
Self::Unstable { .. } => {
unreachable!("`normalize` called when `ExpectationId` is unstable")
}
}
}
} }
impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId { impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
#[inline] #[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
match self { match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { LintExpectationId::Stable {
hir_id,
attr_index,
lint_index: Some(lint_index),
attr_id: _,
} => {
hir_id.hash_stable(hcx, hasher); hir_id.hash_stable(hcx, hasher);
attr_index.hash_stable(hcx, hasher); attr_index.hash_stable(hcx, hasher);
lint_index.hash_stable(hcx, hasher); lint_index.hash_stable(hcx, hasher);
@ -142,9 +160,12 @@ impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectation
#[inline] #[inline]
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
match self { match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { LintExpectationId::Stable {
(*hir_id, *attr_index, *lint_index) hir_id,
} attr_index,
lint_index: Some(lint_index),
attr_id: _,
} => (*hir_id, *attr_index, *lint_index),
_ => { _ => {
unreachable!("HashStable should only be called for a filled `LintExpectationId`") unreachable!("HashStable should only be called for a filled `LintExpectationId`")
} }

View File

@ -62,7 +62,7 @@ use crate::ty::TyCtxt;
use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::fingerprint::Fingerprint;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::definitions::DefPathHash; use rustc_hir::definitions::DefPathHash;
use rustc_hir::HirId; use rustc_hir::{HirId, ItemLocalId};
use rustc_query_system::dep_graph::FingerprintStyle; use rustc_query_system::dep_graph::FingerprintStyle;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;
use std::hash::Hash; use std::hash::Hash;
@ -289,7 +289,7 @@ impl DepNodeExt for DepNode {
let kind = dep_kind_from_label_string(label)?; let kind = dep_kind_from_label_string(label)?;
match kind.fingerprint_style(tcx) { match kind.fingerprint_style(tcx) {
FingerprintStyle::Opaque => Err(()), FingerprintStyle::Opaque | FingerprintStyle::HirId => Err(()),
FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)), FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)),
FingerprintStyle::DefPathHash => { FingerprintStyle::DefPathHash => {
Ok(DepNode::from_def_path_hash(tcx, def_path_hash, kind)) Ok(DepNode::from_def_path_hash(tcx, def_path_hash, kind))
@ -417,7 +417,7 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for (DefId, DefId) {
impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId { impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
#[inline(always)] #[inline(always)]
fn fingerprint_style() -> FingerprintStyle { fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::Opaque FingerprintStyle::HirId
} }
// We actually would not need to specialize the implementation of this // We actually would not need to specialize the implementation of this
@ -426,10 +426,36 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
#[inline(always)] #[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint { fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
let HirId { owner, local_id } = *self; let HirId { owner, local_id } = *self;
let def_path_hash = tcx.def_path_hash(owner.to_def_id()); let def_path_hash = tcx.def_path_hash(owner.to_def_id());
let local_id = Fingerprint::from_smaller_hash(local_id.as_u32().into()); Fingerprint::new(
// `owner` is local, so is completely defined by the local hash
def_path_hash.local_hash(),
local_id.as_u32().into(),
)
}
def_path_hash.0.combine(local_id) #[inline(always)]
fn to_debug_str(&self, tcx: TyCtxt<'tcx>) -> String {
let HirId { owner, local_id } = *self;
format!("{}.{}", tcx.def_path_str(owner.to_def_id()), local_id.as_u32())
}
#[inline(always)]
fn recover(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> Option<Self> {
if dep_node.kind.fingerprint_style(tcx) == FingerprintStyle::HirId {
let (local_hash, local_id) = Fingerprint::from(dep_node.hash).as_value();
let def_path_hash = DefPathHash::new(tcx.sess.local_stable_crate_id(), local_hash);
let owner = tcx
.def_path_hash_to_def_id(def_path_hash, &mut || {
panic!("Failed to extract HirId: {:?} {}", dep_node.kind, dep_node.hash)
})
.expect_local();
let local_id = local_id
.try_into()
.unwrap_or_else(|_| panic!("local id should be u32, found {:?}", local_id));
Some(HirId { owner, local_id: ItemLocalId::from_u32(local_id) })
} else {
None
}
} }
} }

View File

@ -1,20 +1,19 @@
use std::cmp; use std::cmp;
use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::{Diagnostic, DiagnosticId, LintDiagnosticBuilder, MultiSpan}; use rustc_errors::{Diagnostic, DiagnosticId, LintDiagnosticBuilder, MultiSpan};
use rustc_hir::HirId; use rustc_hir::HirId;
use rustc_index::vec::IndexVec;
use rustc_query_system::ich::StableHashingContext;
use rustc_session::lint::{ use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS}, builtin::{self, FORBIDDEN_LINT_GROUPS},
FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, FutureIncompatibilityReason, Level, Lint, LintId,
}; };
use rustc_session::Session; use rustc_session::Session;
use rustc_span::hygiene::MacroKind; use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::{DesugaringKind, ExpnKind}; use rustc_span::source_map::{DesugaringKind, ExpnKind};
use rustc_span::{symbol, Span, Symbol, DUMMY_SP}; use rustc_span::{symbol, Span, Symbol, DUMMY_SP};
use crate::ty::TyCtxt;
/// How a lint level was set. /// How a lint level was set.
#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)] #[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)]
pub enum LintLevelSource { pub enum LintLevelSource {
@ -23,7 +22,12 @@ pub enum LintLevelSource {
Default, Default,
/// Lint level was set by an attribute. /// Lint level was set by an attribute.
Node(Symbol, Span, Option<Symbol> /* RFC 2383 reason */), Node {
name: Symbol,
span: Span,
/// RFC 2383 reason
reason: Option<Symbol>,
},
/// Lint level was set by a command-line flag. /// Lint level was set by a command-line flag.
/// The provided `Level` is the level specified on the command line. /// The provided `Level` is the level specified on the command line.
@ -35,7 +39,7 @@ impl LintLevelSource {
pub fn name(&self) -> Symbol { pub fn name(&self) -> Symbol {
match *self { match *self {
LintLevelSource::Default => symbol::kw::Default, LintLevelSource::Default => symbol::kw::Default,
LintLevelSource::Node(name, _, _) => name, LintLevelSource::Node { name, .. } => name,
LintLevelSource::CommandLine(name, _) => name, LintLevelSource::CommandLine(name, _) => name,
} }
} }
@ -43,7 +47,7 @@ impl LintLevelSource {
pub fn span(&self) -> Span { pub fn span(&self) -> Span {
match *self { match *self {
LintLevelSource::Default => DUMMY_SP, LintLevelSource::Default => DUMMY_SP,
LintLevelSource::Node(_, span, _) => span, LintLevelSource::Node { span, .. } => span,
LintLevelSource::CommandLine(_, _) => DUMMY_SP, LintLevelSource::CommandLine(_, _) => DUMMY_SP,
} }
} }
@ -52,145 +56,115 @@ impl LintLevelSource {
/// A tuple of a lint level and its source. /// A tuple of a lint level and its source.
pub type LevelAndSource = (Level, LintLevelSource); pub type LevelAndSource = (Level, LintLevelSource);
#[derive(Debug, HashStable)] /// Return type for the `shallow_lint_levels_on` query.
pub struct LintLevelSets { ///
pub list: IndexVec<LintStackIndex, LintSet>, /// This map represents the set of allowed lints and allowance levels given
pub lint_cap: Level, /// by the attributes for *a single HirId*.
} #[derive(Default, Debug, HashStable)]
pub struct ShallowLintLevelMap {
rustc_index::newtype_index! {
#[derive(HashStable)]
pub struct LintStackIndex {
const COMMAND_LINE = 0,
}
}
#[derive(Debug, HashStable)]
pub struct LintSet {
// -A,-W,-D flags, a `Symbol` for the flag itself and `Level` for which
// flag.
pub specs: FxHashMap<LintId, LevelAndSource>, pub specs: FxHashMap<LintId, LevelAndSource>,
pub parent: LintStackIndex,
} }
impl LintLevelSets { /// From an initial level and source, verify the effect of special annotations:
pub fn new() -> Self { /// `warnings` lint level and lint caps.
LintLevelSets { list: IndexVec::new(), lint_cap: Level::Forbid } ///
} /// The return of this function is suitable for diagnostics.
pub fn reveal_actual_level(
level: Option<Level>,
src: &mut LintLevelSource,
sess: &Session,
lint: LintId,
probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource),
) -> Level {
// If `level` is none then we actually assume the default level for this lint.
let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition()));
pub fn get_lint_level( // If we're about to issue a warning, check at the last minute for any
&self, // directives against the warnings "lint". If, for example, there's an
lint: &'static Lint, // `allow(warnings)` in scope then we want to respect that instead.
idx: LintStackIndex, //
aux: Option<&FxHashMap<LintId, LevelAndSource>>, // We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
sess: &Session, // triggers in cases (like #80988) where you have `forbid(warnings)`,
) -> LevelAndSource { // and so if we turned that into an error, it'd defeat the purpose of the
let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux); // future compatibility warning.
if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
// If `level` is none then we actually assume the default level for this let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
// lint. if let Some(configured_warning_level) = warnings_level {
let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition())); if configured_warning_level != Level::Warn {
level = configured_warning_level;
// If we're about to issue a warning, check at the last minute for any *src = warnings_src;
// directives against the warnings "lint". If, for example, there's an
// `allow(warnings)` in scope then we want to respect that instead.
//
// We exempt `FORBIDDEN_LINT_GROUPS` from this because it specifically
// triggers in cases (like #80988) where you have `forbid(warnings)`,
// and so if we turned that into an error, it'd defeat the purpose of the
// future compatibility warning.
if level == Level::Warn && LintId::of(lint) != LintId::of(FORBIDDEN_LINT_GROUPS) {
let (warnings_level, warnings_src) =
self.get_lint_id_level(LintId::of(builtin::WARNINGS), idx, aux);
if let Some(configured_warning_level) = warnings_level {
if configured_warning_level != Level::Warn {
level = configured_warning_level;
src = warnings_src;
}
} }
} }
}
// Ensure that we never exceed the `--cap-lints` argument // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
// unless the source is a --force-warn level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src { level
level } else {
} else { cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
cmp::min(level, self.lint_cap) };
};
if let Some(driver_level) = sess.driver_lint_caps.get(&LintId::of(lint)) { if let Some(driver_level) = sess.driver_lint_caps.get(&lint) {
// Ensure that we never exceed driver level. // Ensure that we never exceed driver level.
level = cmp::min(*driver_level, level); level = cmp::min(*driver_level, level);
}
level
}
impl ShallowLintLevelMap {
/// Perform a deep probe in the HIR tree looking for the actual level for the lint.
/// This lint level is not usable for diagnostics, it needs to be corrected by
/// `reveal_actual_level` beforehand.
fn probe_for_lint_level(
&self,
tcx: TyCtxt<'_>,
id: LintId,
start: HirId,
) -> (Option<Level>, LintLevelSource) {
if let Some(&(level, src)) = self.specs.get(&id) {
return (Some(level), src);
} }
for (parent, _) in tcx.hir().parent_iter(start) {
let specs = tcx.shallow_lint_levels_on(parent);
if let Some(&(level, src)) = specs.specs.get(&id) {
return (Some(level), src);
}
}
(None, LintLevelSource::Default)
}
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
pub fn lint_level_id_at_node(
&self,
tcx: TyCtxt<'_>,
lint: LintId,
id: HirId,
) -> (Level, LintLevelSource) {
let (level, mut src) = self.probe_for_lint_level(tcx, lint, id);
let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
self.probe_for_lint_level(tcx, lint, id)
});
debug!(?id, ?level, ?src);
(level, src) (level, src)
} }
}
pub fn get_lint_id_level( impl TyCtxt<'_> {
&self, /// Fetch and return the user-visible lint level for the given lint at the given HirId.
id: LintId, pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
mut idx: LintStackIndex, self.shallow_lint_levels_on(id).lint_level_id_at_node(self, LintId::of(lint), id)
aux: Option<&FxHashMap<LintId, LevelAndSource>>,
) -> (Option<Level>, LintLevelSource) {
if let Some(specs) = aux {
if let Some(&(level, src)) = specs.get(&id) {
return (Some(level), src);
}
}
loop {
let LintSet { ref specs, parent } = self.list[idx];
if let Some(&(level, src)) = specs.get(&id) {
return (Some(level), src);
}
if idx == COMMAND_LINE {
return (None, LintLevelSource::Default);
}
idx = parent;
}
} }
}
#[derive(Debug)] /// Walks upwards from `id` to find a node which might change lint levels with attributes.
pub struct LintLevelMap { /// It stops at `bound` and just returns it if reached.
/// This is a collection of lint expectations as described in RFC 2383, that pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
/// can be fulfilled during this compilation session. This means that at least let hir = self.hir();
/// one expected lint is currently registered in the lint store. while id != bound && self.shallow_lint_levels_on(id).specs.is_empty() {
/// id = hir.get_parent_node(id)
/// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect) }
/// lint level. id
pub lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
pub sets: LintLevelSets,
pub id_to_set: FxHashMap<HirId, LintStackIndex>,
}
impl LintLevelMap {
/// If the `id` was previously registered with `register_id` when building
/// this `LintLevelMap` this returns the corresponding lint level and source
/// of the lint level for the lint provided.
///
/// If the `id` was not previously registered, returns `None`. If `None` is
/// returned then the parent of `id` should be acquired and this function
/// should be called again.
pub fn level_and_source(
&self,
lint: &'static Lint,
id: HirId,
session: &Session,
) -> Option<LevelAndSource> {
self.id_to_set.get(&id).map(|idx| self.sets.get_lint_level(lint, *idx, None, session))
}
}
impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
#[inline]
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
let LintLevelMap { ref sets, ref id_to_set, ref lint_expectations } = *self;
id_to_set.hash_stable(hcx, hasher);
lint_expectations.hash_stable(hcx, hasher);
hcx.while_hashing_spans(true, |hcx| sets.hash_stable(hcx, hasher))
} }
} }
@ -261,11 +235,11 @@ pub fn explain_lint_level_source(
)); ));
} }
} }
LintLevelSource::Node(lint_attr_name, src, reason) => { LintLevelSource::Node { name: lint_attr_name, span, reason, .. } => {
if let Some(rationale) = reason { if let Some(rationale) = reason {
err.note(rationale.as_str()); err.note(rationale.as_str());
} }
err.span_note_once(src, "the lint level is defined here"); err.span_note_once(span, "the lint level is defined here");
if lint_attr_name.as_str() != name { if lint_attr_name.as_str() != name {
let level_str = level.as_str(); let level_str = level.as_str();
err.note_once(&format!( err.note_once(&format!(

View File

@ -273,10 +273,14 @@ rustc_queries! {
separate_provide_extern separate_provide_extern
} }
query lint_levels(_: ()) -> LintLevelMap { query shallow_lint_levels_on(key: HirId) -> rustc_middle::lint::ShallowLintLevelMap {
arena_cache arena_cache
eval_always desc { |tcx| "looking up lint levels for `{}`", key }
desc { "computing the lint levels for items in this crate" } }
query lint_expectations(_: ()) -> Vec<(LintExpectationId, LintExpectation)> {
arena_cache
desc { "computing `#[expect]`ed lints in this crate" }
} }
query parent_module_from_def_id(key: LocalDefId) -> LocalDefId { query parent_module_from_def_id(key: LocalDefId) -> LocalDefId {

View File

@ -4,7 +4,7 @@ use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKind, DepKindStruct}; use crate::dep_graph::{DepGraph, DepKind, DepKindStruct};
use crate::hir::place::Place as HirPlace; use crate::hir::place::Place as HirPlace;
use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos};
use crate::lint::{struct_lint_level, LintLevelSource}; use crate::lint::struct_lint_level;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::resolve_lifetime; use crate::middle::resolve_lifetime;
use crate::middle::stability; use crate::middle::stability;
@ -53,7 +53,7 @@ use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_session::config::{CrateType, OutputFilenames}; use rustc_session::config::{CrateType, OutputFilenames};
use rustc_session::cstore::CrateStoreDyn; use rustc_session::cstore::CrateStoreDyn;
use rustc_session::lint::{Level, Lint}; use rustc_session::lint::Lint;
use rustc_session::Limit; use rustc_session::Limit;
use rustc_session::Session; use rustc_session::Session;
use rustc_span::def_id::{DefPathHash, StableCrateId}; use rustc_span::def_id::{DefPathHash, StableCrateId};
@ -2810,44 +2810,6 @@ impl<'tcx> TyCtxt<'tcx> {
iter.intern_with(|xs| self.intern_bound_variable_kinds(xs)) iter.intern_with(|xs| self.intern_bound_variable_kinds(xs))
} }
/// Walks upwards from `id` to find a node which might change lint levels with attributes.
/// It stops at `bound` and just returns it if reached.
pub fn maybe_lint_level_root_bounded(self, mut id: HirId, bound: HirId) -> HirId {
let hir = self.hir();
loop {
if id == bound {
return bound;
}
if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) {
return id;
}
let next = hir.get_parent_node(id);
if next == id {
bug!("lint traversal reached the root of the crate");
}
id = next;
}
}
pub fn lint_level_at_node(
self,
lint: &'static Lint,
mut id: hir::HirId,
) -> (Level, LintLevelSource) {
let sets = self.lint_levels(());
loop {
if let Some(pair) = sets.level_and_source(lint, id, self.sess) {
return pair;
}
let next = self.hir().get_parent_node(id);
if next == id {
bug!("lint traversal reached the root of the crate");
}
id = next;
}
}
/// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`, /// Emit a lint at `span` from a lint struct (some type that implements `DecorateLint`,
/// typically generated by `#[derive(LintDiagnostic)]`). /// typically generated by `#[derive(LintDiagnostic)]`).
pub fn emit_spanned_lint( pub fn emit_spanned_lint(

View File

@ -1,6 +1,6 @@
use crate::dep_graph; use crate::dep_graph;
use crate::infer::canonical::{self, Canonical}; use crate::infer::canonical::{self, Canonical};
use crate::lint::LintLevelMap; use crate::lint::LintExpectation;
use crate::metadata::ModChild; use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs; use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo}; use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
@ -44,12 +44,14 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId};
use rustc_hir::hir_id::HirId;
use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::lang_items::{LangItem, LanguageItems};
use rustc_hir::{Crate, ItemLocalId, TraitCandidate}; use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec}; use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::cstore::{CrateDepKind, CrateSource}; use rustc_session::cstore::{CrateDepKind, CrateSource};
use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib}; use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib};
use rustc_session::lint::LintExpectationId;
use rustc_session::utils::NativeLibKind; use rustc_session::utils::NativeLibKind;
use rustc_session::Limits; use rustc_session::Limits;
use rustc_span::symbol::Symbol; use rustc_span::symbol::Symbol;

View File

@ -1,6 +1,7 @@
//! Defines the set of legal keys that can be used in queries. //! Defines the set of legal keys that can be used in queries.
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE}; use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
use rustc_hir::hir_id::HirId;
use rustc_middle::infer::canonical::Canonical; use rustc_middle::infer::canonical::Canonical;
use rustc_middle::mir; use rustc_middle::mir;
use rustc_middle::traits; use rustc_middle::traits;
@ -543,3 +544,19 @@ impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
DUMMY_SP DUMMY_SP
} }
} }
impl Key for HirId {
#[inline(always)]
fn query_crate_is_local(&self) -> bool {
true
}
fn default_span(&self, tcx: TyCtxt<'_>) -> Span {
tcx.hir().span(*self)
}
#[inline(always)]
fn key_as_def_id(&self) -> Option<DefId> {
None
}
}

View File

@ -67,6 +67,8 @@ impl<T: DepContext> HasDepContext for T {
pub enum FingerprintStyle { pub enum FingerprintStyle {
/// The fingerprint is actually a DefPathHash. /// The fingerprint is actually a DefPathHash.
DefPathHash, DefPathHash,
/// The fingerprint is actually a HirId.
HirId,
/// Query key was `()` or equivalent, so fingerprint is just zero. /// Query key was `()` or equivalent, so fingerprint is just zero.
Unit, Unit,
/// Some opaque hash. /// Some opaque hash.
@ -77,7 +79,9 @@ impl FingerprintStyle {
#[inline] #[inline]
pub fn reconstructible(self) -> bool { pub fn reconstructible(self) -> bool {
match self { match self {
FingerprintStyle::DefPathHash | FingerprintStyle::Unit => true, FingerprintStyle::DefPathHash | FingerprintStyle::Unit | FingerprintStyle::HirId => {
true
}
FingerprintStyle::Opaque => false, FingerprintStyle::Opaque => false,
} }
} }

View File

@ -3,9 +3,11 @@
#![deny(non_exhaustive_omitted_patterns)] #![deny(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`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
#![allow(non_exhaustive_omitted_patterns)] #![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`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
fn main() { fn main() {
enum Foo { enum Foo {
@ -17,6 +19,8 @@ fn main() {
//~| 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`
//~| 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 { match Foo::A {
Foo::A => {} Foo::A => {}
Foo::B => {} Foo::B => {}
@ -31,4 +35,5 @@ fn main() {
} }
//~^^^ 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`
//~| WARNING unknown lint: `non_exhaustive_omitted_patterns`
} }

View File

@ -10,7 +10,7 @@ LL | #![deny(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:6:1 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1
| |
LL | #![allow(non_exhaustive_omitted_patterns)] LL | #![allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -20,7 +20,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
| |
LL | #[allow(non_exhaustive_omitted_patterns)] LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -30,7 +30,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
| |
LL | #[allow(non_exhaustive_omitted_patterns)] LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -40,7 +40,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:29:9 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:33:9
| |
LL | #[warn(non_exhaustive_omitted_patterns)] LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -60,7 +60,7 @@ LL | #![deny(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:6:1 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1
| |
LL | #![allow(non_exhaustive_omitted_patterns)] LL | #![allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -70,7 +70,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
| |
LL | #[allow(non_exhaustive_omitted_patterns)] LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -80,7 +80,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:15:5 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
| |
LL | #[allow(non_exhaustive_omitted_patterns)] LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -90,7 +90,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns` warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:29:9 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:33:9
| |
LL | #[warn(non_exhaustive_omitted_patterns)] LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -100,13 +100,13 @@ LL | #[warn(non_exhaustive_omitted_patterns)]
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable = help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
error[E0004]: non-exhaustive patterns: `Foo::C` not covered error[E0004]: non-exhaustive patterns: `Foo::C` not covered
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:20:11 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:24:11
| |
LL | match Foo::A { LL | match Foo::A {
| ^^^^^^ pattern `Foo::C` not covered | ^^^^^^ pattern `Foo::C` not covered
| |
note: `Foo` defined here note: `Foo` defined here
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:12:15 --> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:14:15
| |
LL | enum Foo { LL | enum Foo {
| --- | ---
@ -119,6 +119,56 @@ LL ~ Foo::B => {}
LL + Foo::C => todo!() LL + Foo::C => todo!()
| |
error: aborting due to previous error; 10 warnings emitted warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:3:1
|
LL | #![deny(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `non_exhaustive_omitted_patterns` lint is unstable
= note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:7:1
|
LL | #![allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `non_exhaustive_omitted_patterns` lint is unstable
= note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
|
LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `non_exhaustive_omitted_patterns` lint is unstable
= note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
|
LL | #[allow(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `non_exhaustive_omitted_patterns` lint is unstable
= note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
warning: unknown lint: `non_exhaustive_omitted_patterns`
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:33:9
|
LL | #[warn(non_exhaustive_omitted_patterns)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: the `non_exhaustive_omitted_patterns` lint is unstable
= note: see issue #89554 <https://github.com/rust-lang/rust/issues/89554> for more information
= help: add `#![feature(non_exhaustive_omitted_patterns_lint)]` to the crate attributes to enable
error: aborting due to previous error; 15 warnings emitted
For more information about this error, try `rustc --explain E0004`. For more information about this error, try `rustc --explain E0004`.

View File

@ -1,11 +1,3 @@
warning: denote infinite loops with `loop { ... }`
--> $DIR/force_warn_expected_lints_fulfilled.rs:10:5
|
LL | while true {
| ^^^^^^^^^^ help: use `loop`
|
= note: requested on the command line with `--force-warn while-true`
warning: unused variable: `x` warning: unused variable: `x`
--> $DIR/force_warn_expected_lints_fulfilled.rs:20:9 --> $DIR/force_warn_expected_lints_fulfilled.rs:20:9
| |
@ -36,5 +28,13 @@ LL | let mut what_does_the_fox_say = "*ding* *deng* *dung*";
| |
= note: requested on the command line with `--force-warn unused-mut` = note: requested on the command line with `--force-warn unused-mut`
warning: denote infinite loops with `loop { ... }`
--> $DIR/force_warn_expected_lints_fulfilled.rs:10:5
|
LL | while true {
| ^^^^^^^^^^ help: use `loop`
|
= note: requested on the command line with `--force-warn while-true`
warning: 5 warnings emitted warning: 5 warnings emitted