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.
let lint_index = expectation_id.get_lint_index();
expectation_id.set_lint_index(None);
let mut stable_id = *unstable_to_stable
let mut stable_id = unstable_to_stable
.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);
*expectation_id = stable_id;

View File

@ -1167,7 +1167,7 @@ impl HandlerInner {
if let Some(expectation_id) = diagnostic.level.get_expectation_id() {
self.suppressed_expected_diag = true;
self.fulfilled_expectations.insert(expectation_id);
self.fulfilled_expectations.insert(expectation_id.normalize());
}
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.
pub struct EarlyContext<'a> {
pub builder: LintLevelsBuilder<'a>,
pub builder: LintLevelsBuilder<'a, crate::levels::TopDown>,
pub buffered: LintBuffer,
}

View File

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

View File

@ -16,8 +16,10 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
return;
}
let lint_expectations = tcx.lint_expectations(());
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 {
// 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_hir as hir;
use rustc_hir::{intravisit, HirId};
use rustc_index::vec::IndexVec;
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{
struct_lint_level, LevelAndSource, LintExpectation, LintLevelMap, LintLevelSets,
LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE,
reveal_actual_level, struct_lint_level, LevelAndSource, LintExpectation, LintLevelSource,
ShallowLintLevelMap,
};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
@ -27,47 +28,292 @@ use crate::errors::{
UnknownToolInScopedLint,
};
fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
let store = unerased_lint_store(tcx);
let levels =
LintLevelsBuilder::new(tcx.sess, false, &store, &tcx.resolutions(()).registered_tools);
let mut builder = LintLevelMapBuilder { levels, tcx };
let krate = tcx.hir().krate();
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()
/// Collection of lint levels for the whole crate.
/// This is used by AST-based lints, which do not
/// wait until we have built HIR to be emitted.
#[derive(Debug)]
struct LintLevelSets {
/// Linked list of specifications.
list: IndexVec<LintStackIndex, LintSet>,
}
pub struct LintLevelsBuilder<'s> {
sess: &'s Session,
lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
/// Each expectation has a stable and an unstable identifier. This map
/// is used to map from unstable to stable [`LintExpectationId`]s.
expectation_id_map: FxHashMap<LintExpectationId, LintExpectationId>,
rustc_index::newtype_index! {
struct LintStackIndex {
ENCODABLE = custom, // we don't need encoding
const COMMAND_LINE = 0,
}
}
/// 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,
id_to_set: FxHashMap<HirId, 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,
store: &'s LintStore,
registered_tools: &'s RegisteredTools,
}
pub struct BuilderPush {
pub(crate) struct BuilderPush {
prev: LintStackIndex,
pub changed: bool,
}
impl<'s> LintLevelsBuilder<'s> {
pub fn new(
impl<'s> LintLevelsBuilder<'s, TopDown> {
pub(crate) fn new(
sess: &'s Session,
warn_about_weird_lints: bool,
store: &'s LintStore,
@ -75,20 +321,66 @@ impl<'s> LintLevelsBuilder<'s> {
) -> Self {
let mut builder = LintLevelsBuilder {
sess,
lint_expectations: Default::default(),
expectation_id_map: Default::default(),
sets: LintLevelSets::new(),
cur: COMMAND_LINE,
id_to_set: Default::default(),
provider: TopDown { sets: LintLevelSets::new(), cur: COMMAND_LINE },
warn_about_weird_lints,
store,
registered_tools,
};
builder.process_command_line(sess, store);
assert_eq!(builder.sets.list.len(), 1);
builder.process_command_line();
assert_eq!(builder.provider.sets.list.len(), 1);
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 {
self.sess
}
@ -98,24 +390,20 @@ impl<'s> LintLevelsBuilder<'s> {
}
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> {
&mut self.sets.list[self.cur].specs
self.provider.current_specs_mut()
}
fn process_command_line(&mut self, sess: &Session, store: &LintStore) {
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);
fn add_command_line(&mut self) {
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);
let orig_level = level;
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
continue
};
@ -138,9 +426,11 @@ impl<'s> LintLevelsBuilder<'s> {
/// 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, id: LintId, (level, src): LevelAndSource) {
let (old_level, old_src) =
self.sets.get_lint_level(id.lint, self.cur, Some(self.current_specs()), &self.sess);
fn insert_spec(&mut self, id: LintId, (mut level, src): LevelAndSource) {
let (old_level, old_src) = self.provider.get_lint_level(id.lint, &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
// a forbid level. Note that this is not necessarily true even with a
// `#[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 fcw_warning = match old_src {
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),
};
debug!(
@ -178,8 +468,8 @@ impl<'s> LintLevelsBuilder<'s> {
id.to_string()
));
}
LintLevelSource::Node(_, forbid_source_span, reason) => {
diag.span_label(forbid_source_span, "`forbid` level set here");
LintLevelSource::Node { span, reason, .. } => {
diag.span_label(span, "`forbid` level set here");
if let Some(rationale) = reason {
diag.note(rationale.as_str());
}
@ -199,11 +489,8 @@ impl<'s> LintLevelsBuilder<'s> {
LintLevelSource::Default => {
OverruledAttributeSub::DefaultSource { id: id.to_string() }
}
LintLevelSource::Node(_, forbid_source_span, reason) => {
OverruledAttributeSub::NodeSource {
span: forbid_source_span,
reason,
}
LintLevelSource::Node { span, reason, .. } => {
OverruledAttributeSub::NodeSource { span, reason }
}
LintLevelSource::CommandLine(_, _) => {
OverruledAttributeSub::CommandLineSource
@ -256,29 +543,7 @@ impl<'s> LintLevelsBuilder<'s> {
};
}
/// 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.cur;
self.cur = self.sets.list.push(LintSet { specs: FxHashMap::default(), parent: prev });
fn add(&mut self, attrs: &[ast::Attribute], is_crate_node: bool, source_hir_id: Option<HirId>) {
let sess = self.sess;
for (attr_index, attr) in attrs.iter().enumerate() {
if attr.has_name(sym::automatically_derived) {
@ -293,7 +558,17 @@ impl<'s> LintLevelsBuilder<'s> {
None => continue,
// 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 => {
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)
}
@ -408,7 +683,7 @@ impl<'s> LintLevelsBuilder<'s> {
[lint] => *lint == LintId::of(UNFULFILLED_LINT_EXPECTATIONS),
_ => false,
};
self.lint_expectations.push((
self.provider.push_expectation(
expect_id,
LintExpectation::new(
reason,
@ -416,13 +691,19 @@ impl<'s> LintLevelsBuilder<'s> {
is_unfulfilled_lint_expectations,
tool_name,
),
));
}
let src = LintLevelSource::Node(
meta_item.path.segments.last().expect("empty lint name").ident.name,
sp,
reason,
);
}
let src = LintLevelSource::Node {
name: meta_item
.path
.segments
.last()
.expect("empty lint name")
.ident
.name,
span: sp,
reason,
};
for &id in *ids {
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
@ -435,31 +716,26 @@ impl<'s> LintLevelsBuilder<'s> {
Ok(ids) => {
let complete_name =
&format!("{}::{}", tool_ident.unwrap().name, name);
let src = LintLevelSource::Node(
Symbol::intern(complete_name),
sp,
let src = LintLevelSource::Node {
name: Symbol::intern(complete_name),
span: sp,
reason,
);
};
for &id in ids {
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
}
}
if let Level::Expect(expect_id) = level {
self.lint_expectations.push((
self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
));
);
}
}
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(self.current_specs()),
&sess,
);
let (lvl, src) = self.provider.get_lint_level(lint, &sess);
struct_lint_level(
self.sess,
lint,
@ -483,19 +759,19 @@ impl<'s> LintLevelsBuilder<'s> {
},
);
let src = LintLevelSource::Node(
Symbol::intern(&new_lint_name),
sp,
let src = LintLevelSource::Node {
name: Symbol::intern(&new_lint_name),
span: sp,
reason,
);
};
for id in ids {
self.insert_spec(*id, (level, src));
}
if let Level::Expect(expect_id) = level {
self.lint_expectations.push((
self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
));
);
}
}
Err((None, _)) => {
@ -521,12 +797,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(self.current_specs()),
&sess,
);
let (renamed_lint_level, src) = self.provider.get_lint_level(lint, &sess);
struct_lint_level(
self.sess,
lint,
@ -549,12 +820,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(self.current_specs()),
self.sess,
);
let (level, src) = self.provider.get_lint_level(lint, 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)
@ -583,17 +849,21 @@ impl<'s> LintLevelsBuilder<'s> {
if let CheckLintNameResult::Ok(ids) =
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 {
if self.check_gated_lint(id, attr.span) {
self.insert_spec(id, (level, src));
}
}
if let Level::Expect(expect_id) = level {
self.lint_expectations.push((
self.provider.push_expectation(
expect_id,
LintExpectation::new(reason, sp, false, tool_name),
));
);
}
} else {
panic!("renamed lint does not exist: {}", new_name);
@ -608,13 +878,12 @@ impl<'s> LintLevelsBuilder<'s> {
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
};
let lint = builtin::UNUSED_ATTRIBUTES;
let (lint_level, lint_src) =
self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), self.sess);
let (lint_level, lint_src) = self.provider.get_lint_level(lint, &self.sess);
struct_lint_level(
self.sess,
lint,
@ -634,32 +903,13 @@ impl<'s> LintLevelsBuilder<'s> {
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.
///
/// 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 {
if let Some(feature) = lint_id.lint.feature_gate {
if !self.sess.features_untracked().enabled(feature) {
@ -678,19 +928,14 @@ impl<'s> LintLevelsBuilder<'s> {
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.
pub fn lint_level(&self, lint: &'static Lint) -> (Level, LintLevelSource) {
self.sets.get_lint_level(lint, self.cur, None, self.sess)
pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.provider.get_lint_level(lint, self.sess)
}
/// Used to emit a lint-related diagnostic based on the current state of
/// this lint context.
pub fn struct_lint(
pub(crate) fn struct_lint(
&self,
lint: &'static Lint,
span: Option<MultiSpan>,
@ -699,141 +944,8 @@ impl<'s> LintLevelsBuilder<'s> {
let (level, src) = self.lint_level(lint);
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> {
levels: LintLevelsBuilder<'tcx>,
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;
pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { shallow_lint_levels_on, lint_expectations, ..*providers };
}

View File

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

View File

@ -92,7 +92,7 @@ pub enum LintExpectationId {
/// stable and can be cached. The additional index ensures that nodes with
/// several expectations can correctly match diagnostics to the individual
/// 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 {
@ -116,13 +116,31 @@ impl LintExpectationId {
*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 {
#[inline]
fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) {
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);
attr_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]
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType {
match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
(*hir_id, *attr_index, *lint_index)
}
LintExpectationId::Stable {
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`")
}

View File

@ -62,7 +62,7 @@ use crate::ty::TyCtxt;
use rustc_data_structures::fingerprint::Fingerprint;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::definitions::DefPathHash;
use rustc_hir::HirId;
use rustc_hir::{HirId, ItemLocalId};
use rustc_query_system::dep_graph::FingerprintStyle;
use rustc_span::symbol::Symbol;
use std::hash::Hash;
@ -289,7 +289,7 @@ impl DepNodeExt for DepNode {
let kind = dep_kind_from_label_string(label)?;
match kind.fingerprint_style(tcx) {
FingerprintStyle::Opaque => Err(()),
FingerprintStyle::Opaque | FingerprintStyle::HirId => Err(()),
FingerprintStyle::Unit => Ok(DepNode::new_no_params(tcx, kind)),
FingerprintStyle::DefPathHash => {
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 {
#[inline(always)]
fn fingerprint_style() -> FingerprintStyle {
FingerprintStyle::Opaque
FingerprintStyle::HirId
}
// We actually would not need to specialize the implementation of this
@ -426,10 +426,36 @@ impl<'tcx> DepNodeParams<TyCtxt<'tcx>> for HirId {
#[inline(always)]
fn to_fingerprint(&self, tcx: TyCtxt<'tcx>) -> Fingerprint {
let HirId { owner, local_id } = *self;
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 rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_errors::{Diagnostic, DiagnosticId, LintDiagnosticBuilder, MultiSpan};
use rustc_hir::HirId;
use rustc_index::vec::IndexVec;
use rustc_query_system::ich::StableHashingContext;
use rustc_session::lint::{
builtin::{self, FORBIDDEN_LINT_GROUPS},
FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId,
FutureIncompatibilityReason, Level, Lint, LintId,
};
use rustc_session::Session;
use rustc_span::hygiene::MacroKind;
use rustc_span::source_map::{DesugaringKind, ExpnKind};
use rustc_span::{symbol, Span, Symbol, DUMMY_SP};
use crate::ty::TyCtxt;
/// How a lint level was set.
#[derive(Clone, Copy, PartialEq, Eq, HashStable, Debug)]
pub enum LintLevelSource {
@ -23,7 +22,12 @@ pub enum LintLevelSource {
Default,
/// 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.
/// The provided `Level` is the level specified on the command line.
@ -35,7 +39,7 @@ impl LintLevelSource {
pub fn name(&self) -> Symbol {
match *self {
LintLevelSource::Default => symbol::kw::Default,
LintLevelSource::Node(name, _, _) => name,
LintLevelSource::Node { name, .. } => name,
LintLevelSource::CommandLine(name, _) => name,
}
}
@ -43,7 +47,7 @@ impl LintLevelSource {
pub fn span(&self) -> Span {
match *self {
LintLevelSource::Default => DUMMY_SP,
LintLevelSource::Node(_, span, _) => span,
LintLevelSource::Node { span, .. } => span,
LintLevelSource::CommandLine(_, _) => DUMMY_SP,
}
}
@ -52,45 +56,28 @@ impl LintLevelSource {
/// A tuple of a lint level and its source.
pub type LevelAndSource = (Level, LintLevelSource);
#[derive(Debug, HashStable)]
pub struct LintLevelSets {
pub list: IndexVec<LintStackIndex, LintSet>,
pub lint_cap: Level,
}
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.
/// Return type for the `shallow_lint_levels_on` query.
///
/// This map represents the set of allowed lints and allowance levels given
/// by the attributes for *a single HirId*.
#[derive(Default, Debug, HashStable)]
pub struct ShallowLintLevelMap {
pub specs: FxHashMap<LintId, LevelAndSource>,
pub parent: LintStackIndex,
}
impl LintLevelSets {
pub fn new() -> Self {
LintLevelSets { list: IndexVec::new(), lint_cap: Level::Forbid }
}
pub fn get_lint_level(
&self,
lint: &'static Lint,
idx: LintStackIndex,
aux: Option<&FxHashMap<LintId, LevelAndSource>>,
/// From an initial level and source, verify the effect of special annotations:
/// `warnings` lint level and lint caps.
///
/// The return of this function is suitable for diagnostics.
pub fn reveal_actual_level(
level: Option<Level>,
src: &mut LintLevelSource,
sess: &Session,
) -> LevelAndSource {
let (level, mut src) = self.get_lint_id_level(LintId::of(lint), idx, aux);
// If `level` is none then we actually assume the default level for this
// lint.
let mut level = level.unwrap_or_else(|| lint.default_level(sess.edition()));
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()));
// If we're about to issue a warning, check at the last minute for any
// directives against the warnings "lint". If, for example, there's an
@ -100,97 +87,84 @@ impl LintLevelSets {
// 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 level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
if let Some(configured_warning_level) = warnings_level {
if configured_warning_level != Level::Warn {
level = configured_warning_level;
src = warnings_src;
*src = warnings_src;
}
}
}
// Ensure that we never exceed the `--cap-lints` argument
// unless the source is a --force-warn
// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src {
level
} else {
cmp::min(level, self.lint_cap)
cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
};
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.
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)
}
pub fn get_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;
}
}
}
#[derive(Debug)]
pub struct LintLevelMap {
/// This is a collection of lint expectations as described in RFC 2383, that
/// can be fulfilled during this compilation session. This means that at least
/// one expected lint is currently registered in the lint store.
///
/// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect)
/// lint level.
pub lint_expectations: Vec<(LintExpectationId, LintExpectation)>,
pub sets: LintLevelSets,
pub id_to_set: FxHashMap<HirId, LintStackIndex>,
impl TyCtxt<'_> {
/// Fetch and return the user-visible lint level for the given lint at the given HirId.
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) {
self.shallow_lint_levels_on(id).lint_level_id_at_node(self, LintId::of(lint), id)
}
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))
/// 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();
while id != bound && self.shallow_lint_levels_on(id).specs.is_empty() {
id = hir.get_parent_node(id)
}
}
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))
id
}
}
@ -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 {
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 {
let level_str = level.as_str();
err.note_once(&format!(

View File

@ -273,10 +273,14 @@ rustc_queries! {
separate_provide_extern
}
query lint_levels(_: ()) -> LintLevelMap {
query shallow_lint_levels_on(key: HirId) -> rustc_middle::lint::ShallowLintLevelMap {
arena_cache
eval_always
desc { "computing the lint levels for items in this crate" }
desc { |tcx| "looking up lint levels for `{}`", key }
}
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 {

View File

@ -4,7 +4,7 @@ use crate::arena::Arena;
use crate::dep_graph::{DepGraph, DepKind, DepKindStruct};
use crate::hir::place::Place as HirPlace;
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::resolve_lifetime;
use crate::middle::stability;
@ -53,7 +53,7 @@ use rustc_query_system::ich::StableHashingContext;
use rustc_serialize::opaque::{FileEncodeResult, FileEncoder};
use rustc_session::config::{CrateType, OutputFilenames};
use rustc_session::cstore::CrateStoreDyn;
use rustc_session::lint::{Level, Lint};
use rustc_session::lint::Lint;
use rustc_session::Limit;
use rustc_session::Session;
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))
}
/// 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`,
/// typically generated by `#[derive(LintDiagnostic)]`).
pub fn emit_spanned_lint(

View File

@ -1,6 +1,6 @@
use crate::dep_graph;
use crate::infer::canonical::{self, Canonical};
use crate::lint::LintLevelMap;
use crate::lint::LintExpectation;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
@ -44,12 +44,14 @@ use rustc_errors::ErrorGuaranteed;
use rustc_hir as hir;
use rustc_hir::def::DefKind;
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::{Crate, ItemLocalId, TraitCandidate};
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion};
use rustc_session::cstore::{CrateDepKind, CrateSource};
use rustc_session::cstore::{ExternCrate, ForeignModule, LinkagePreference, NativeLib};
use rustc_session::lint::LintExpectationId;
use rustc_session::utils::NativeLibKind;
use rustc_session::Limits;
use rustc_span::symbol::Symbol;

View File

@ -1,6 +1,7 @@
//! 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::hir_id::HirId;
use rustc_middle::infer::canonical::Canonical;
use rustc_middle::mir;
use rustc_middle::traits;
@ -543,3 +544,19 @@ impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
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 {
/// The fingerprint is actually a DefPathHash.
DefPathHash,
/// The fingerprint is actually a HirId.
HirId,
/// Query key was `()` or equivalent, so fingerprint is just zero.
Unit,
/// Some opaque hash.
@ -77,7 +79,9 @@ impl FingerprintStyle {
#[inline]
pub fn reconstructible(self) -> bool {
match self {
FingerprintStyle::DefPathHash | FingerprintStyle::Unit => true,
FingerprintStyle::DefPathHash | FingerprintStyle::Unit | FingerprintStyle::HirId => {
true
}
FingerprintStyle::Opaque => false,
}
}

View File

@ -3,9 +3,11 @@
#![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`
#![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`
fn main() {
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`
match Foo::A {
Foo::A => {}
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`
}

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
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -20,7 +20,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)]
= 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:15:5
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
|
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
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -40,7 +40,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= 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:29:9
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:33:9
|
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
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -70,7 +70,7 @@ LL | #![allow(non_exhaustive_omitted_patterns)]
= 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:15:5
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:17:5
|
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
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)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -90,7 +90,7 @@ LL | #[allow(non_exhaustive_omitted_patterns)]
= 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:29:9
--> $DIR/feature-gate-non_exhaustive_omitted_patterns_lint.rs:33:9
|
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
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 {
| ^^^^^^ pattern `Foo::C` not covered
|
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 {
| ---
@ -119,6 +119,56 @@ LL ~ Foo::B => {}
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`.

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`
--> $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`
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