Split LintExpectationId

`LintExpectationId` has two variants, `Unstable` and `Stable`. There are
some places where both variants are possible, but there are also places
where only one of `Unstable` or `Stable` is possible.

This commit encodes this into the type system by introducing
new types `UnstableLintExpectationId` and `StableLintExpectationId`. The
variants of `LintExpectationId` now enclose these. This makes it clearer
what values are possible where.

Other things of note:
- `LintLevelsProvider` gets an associated type and some method changes.
- `LintContext` gets an associated type.
- `LevelSpec` is made generic. `UnstableLevelSpec` and `StableLevelSpec`
  typedefs are added.
- The unstable types are now guaranteed by the type system to never be
  stably hashed. Previously this was a runtime check.
This commit is contained in:
Nicholas Nethercote
2026-05-15 17:58:21 +10:00
parent 40332688e0
commit 22cf1fb6fe
9 changed files with 210 additions and 160 deletions
+3 -3
View File
@@ -26,7 +26,7 @@
use rustc_macros::{Decodable, Encodable};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::lint::LevelSpec;
use rustc_middle::lint::StableLevelSpec;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Dependencies;
use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -342,8 +342,8 @@ pub fn deserialize_rlink(
/// Instead, encode exactly the information we need.
#[derive(Copy, Clone, Debug, Encodable, Decodable)]
pub struct CodegenLintLevelSpecs {
linker_messages: LevelSpec,
linker_info: LevelSpec,
linker_messages: StableLevelSpec,
linker_info: StableLevelSpec,
}
impl CodegenLintLevelSpecs {
+16 -7
View File
@@ -20,14 +20,17 @@
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc_hir::{Pat, PatKind};
use rustc_middle::bug;
use rustc_middle::lint::LevelSpec;
use rustc_middle::lint::{LevelSpec, StableLevelSpec, UnstableLevelSpec};
use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
use rustc_middle::ty::{
self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode, Unnormalized,
};
use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintExpectationId, LintId};
use rustc_session::lint::{
FutureIncompatibleInfo, Lint, LintExpectationId, LintId, StableLintExpectationId,
UnstableLintExpectationId,
};
use rustc_session::{DynLintStore, Session};
use rustc_span::edit_distance::find_best_match_for_names;
use rustc_span::{Ident, Span, Symbol, sym};
@@ -510,6 +513,8 @@ pub struct EarlyContext<'a> {
}
pub trait LintContext {
type LintExpectationId: Copy + Into<LintExpectationId>;
fn sess(&self) -> &Session;
// FIXME: These methods should not take an Into<MultiSpan> -- instead, callers should need to
@@ -538,7 +543,7 @@ fn emit_span_lint<S: Into<MultiSpan>>(
}
/// This returns the lint level spec for the given lint at the current location.
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec;
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<Self::LintExpectationId>;
/// This function can be used to manually fulfill an expectation. This can
/// be used for lints which contain several spans, and should be suppressed,
@@ -547,7 +552,7 @@ fn emit_span_lint<S: Into<MultiSpan>>(
/// Note that this function should only be called for [`LintExpectationId`]s
/// retrieved from the current lint pass. Buffered or manually created ids can
/// cause ICEs.
fn fulfill_expectation(&self, expectation: LintExpectationId) {
fn fulfill_expectation(&self, expectation: Self::LintExpectationId) {
// We need to make sure that submitted expectation ids are correctly fulfilled suppressed
// and stored between compilation sessions. To not manually do these steps, we simply create
// a dummy diagnostic and emit it as usual, which will be suppressed and stored like a
@@ -556,7 +561,7 @@ fn fulfill_expectation(&self, expectation: LintExpectationId) {
.dcx()
.struct_expect(
"this is a dummy diagnostic, to submit and store an expectation",
expectation,
expectation.into(),
)
.emit();
}
@@ -585,6 +590,8 @@ pub(crate) fn new(
}
impl<'tcx> LintContext for LateContext<'tcx> {
type LintExpectationId = StableLintExpectationId;
/// Gets the overall compiler `Session` object.
fn sess(&self) -> &Session {
self.tcx.sess
@@ -604,12 +611,14 @@ fn opt_span_lint<S: Into<MultiSpan>>(
}
}
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
fn get_lint_level_spec(&self, lint: &'static Lint) -> StableLevelSpec {
self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs)
}
}
impl LintContext for EarlyContext<'_> {
type LintExpectationId = UnstableLintExpectationId;
/// Gets the overall compiler `Session` object.
fn sess(&self) -> &Session {
self.builder.sess()
@@ -624,7 +633,7 @@ fn opt_span_lint<S: Into<MultiSpan>>(
self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator)
}
fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
fn get_lint_level_spec(&self, lint: &'static Lint) -> UnstableLevelSpec {
self.builder.lint_level_spec(lint)
}
}
+11 -19
View File
@@ -2,8 +2,8 @@
use rustc_middle::lint::LintExpectation;
use rustc_middle::query::Providers;
use rustc_middle::ty::TyCtxt;
use rustc_session::lint::LintExpectationId;
use rustc_session::lint::builtin::UNFULFILLED_LINT_EXPECTATIONS;
use rustc_session::lint::{LintExpectationId, StableLintExpectationId};
use rustc_span::Symbol;
use crate::lints::{Expectation, ExpectationNote};
@@ -12,7 +12,7 @@ pub(crate) fn provide(providers: &mut Providers) {
*providers = Providers { lint_expectations, check_expectations, ..*providers };
}
fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(LintExpectationId, LintExpectation)> {
fn lint_expectations(tcx: TyCtxt<'_>, (): ()) -> Vec<(StableLintExpectationId, LintExpectation)> {
let krate = tcx.hir_crate_items(());
let mut expectations = Vec::new();
@@ -31,30 +31,22 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
// Turn a `LintExpectationId` into a `(AttrId, lint_index)` pair.
let canonicalize_id = |expect_id: &LintExpectationId| {
match *expect_id {
LintExpectationId::Unstable { attr_id, lint_index: Some(lint_index) } => {
(attr_id, lint_index)
}
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
let (attr_id, lint_index) = match *expect_id {
LintExpectationId::Unstable(id) => (id.attr_id, id.lint_index),
LintExpectationId::Stable(id) => {
// We are an `eval_always` query, so looking at the attribute's `AttrId` is ok.
let attr_id = tcx.hir_attrs(hir_id)[attr_index as usize].id();
(attr_id, lint_index)
(tcx.hir_attrs(id.hir_id)[id.attr_index as usize].id(), id.lint_index)
}
_ => panic!("fulfilled expectations must have a lint index"),
}
};
(attr_id, lint_index.expect("fulfilled expectations must have a lint index"))
};
let fulfilled_expectations: FxHashSet<_> =
fulfilled_expectations.iter().map(canonicalize_id).collect();
for (expect_id, expectation) in lint_expectations {
// This check will always be true, since `lint_expectations` only holds stable ids
let LintExpectationId::Stable { hir_id, .. } = expect_id else {
unreachable!("at this stage all `LintExpectationId`s are stable");
};
let expect_id = canonicalize_id(expect_id);
let hir_id = expect_id.hir_id;
let expect_id = canonicalize_id(&LintExpectationId::Stable(*expect_id));
if !fulfilled_expectations.contains(&expect_id)
&& tool_filter.is_none_or(|filter| expectation.lint_tool == Some(filter))
@@ -63,7 +55,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
let note = expectation.is_unfulfilled_lint_expectations;
tcx.emit_node_span_lint(
UNFULFILLED_LINT_EXPECTATIONS,
*hir_id,
hir_id,
expectation.emission_span,
Expectation { rationale, note },
);
+93 -69
View File
@@ -1,3 +1,5 @@
use std::fmt::Debug;
use rustc_ast as ast;
use rustc_ast::attr::AttributeExt;
use rustc_ast_pretty::pprust;
@@ -11,8 +13,8 @@
use rustc_index::IndexVec;
use rustc_middle::hir::nested_filter;
use rustc_middle::lint::{
LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base,
reveal_actual_level_spec,
LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, StableLevelSpec,
UnstableLevelSpec, emit_lint_base, reveal_actual_level_spec,
};
use rustc_middle::query::Providers;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
@@ -21,8 +23,10 @@
self, FORBIDDEN_LINT_GROUPS, RENAMED_AND_REMOVED_LINTS, SINGLE_USE_LIFETIMES,
UNFULFILLED_LINT_EXPECTATIONS, UNKNOWN_LINTS, UNUSED_ATTRIBUTES,
};
use rustc_session::lint::{Level, Lint, LintExpectationId, LintId};
use rustc_span::{DUMMY_SP, Span, Symbol, sym};
use rustc_session::lint::{
Level, Lint, LintExpectationId, LintId, StableLintExpectationId, UnstableLintExpectationId,
};
use rustc_span::{AttrId, DUMMY_SP, Span, Symbol, sym};
use tracing::{debug, instrument};
use crate::builtin::MISSING_DOCS;
@@ -64,9 +68,7 @@ struct LintStackIndex {
/// to find the specifications for a given lint.
#[derive(Debug)]
struct LintSet {
// -A,-W,-D flags, a `Symbol` for the flag itself and `LevelSpec` for which
// flag.
specs: FxIndexMap<LintId, LevelSpec>,
specs: FxIndexMap<LintId, UnstableLevelSpec>,
parent: LintStackIndex,
}
@@ -79,9 +81,9 @@ fn get_lint_level_spec(
&self,
lint: &'static Lint,
idx: LintStackIndex,
aux: Option<&FxIndexMap<LintId, LevelSpec>>,
aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
sess: &Session,
) -> LevelSpec {
) -> UnstableLevelSpec {
reveal_actual_level_spec(sess, LintId::of(lint), |id| {
self.raw_lint_level_spec(id, idx, aux)
})
@@ -91,8 +93,8 @@ fn raw_lint_level_spec(
&self,
id: LintId,
mut idx: LintStackIndex,
aux: Option<&FxIndexMap<LintId, LevelSpec>>,
) -> Option<LevelSpec> {
aux: Option<&FxIndexMap<LintId, UnstableLevelSpec>>,
) -> Option<UnstableLevelSpec> {
if let Some(specs) = aux
&& let Some(level_spec) = specs.get(&id)
{
@@ -213,26 +215,53 @@ pub struct TopDown {
}
pub trait LintLevelsProvider {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec>;
fn insert(&mut self, id: LintId, level_spec: LevelSpec);
fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> LevelSpec;
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation);
type LintExpectationId: Copy + Debug + Into<LintExpectationId>;
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec<Self::LintExpectationId>>;
fn insert(&mut self, id: LintId, level_spec: LevelSpec<Self::LintExpectationId>);
fn get_lint_level_spec(
&self,
lint: &'static Lint,
sess: &Session,
) -> LevelSpec<Self::LintExpectationId>;
fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation);
fn mk_lint_expectation_id(
&self,
attr_id: AttrId,
attr_index: usize,
lint_index: Option<u16>,
) -> Self::LintExpectationId;
}
impl LintLevelsProvider for TopDown {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
type LintExpectationId = UnstableLintExpectationId;
fn current_specs(&self) -> &FxIndexMap<LintId, UnstableLevelSpec> {
&self.sets.list[self.cur].specs
}
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
fn insert(&mut self, id: LintId, level_spec: UnstableLevelSpec) {
self.sets.list[self.cur].specs.insert(id, level_spec);
}
fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> LevelSpec {
fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> UnstableLevelSpec {
self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess)
}
fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {}
fn push_expectation(&mut self, _: Self::LintExpectationId, _: LintExpectation) {}
fn mk_lint_expectation_id(
&self,
attr_id: AttrId,
_attr_index: usize,
lint_index: Option<u16>,
) -> Self::LintExpectationId {
UnstableLintExpectationId { attr_id, lint_index }
}
}
struct LintLevelQueryMap<'tcx> {
@@ -240,33 +269,44 @@ struct LintLevelQueryMap<'tcx> {
cur: HirId,
specs: ShallowLintLevelMap,
/// Empty hash map to simplify code.
empty: FxIndexMap<LintId, LevelSpec>,
empty: FxIndexMap<LintId, StableLevelSpec>,
attrs: &'tcx hir::AttributeMap<'tcx>,
}
impl LintLevelsProvider for LintLevelQueryMap<'_> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
type LintExpectationId = StableLintExpectationId;
fn current_specs(&self) -> &FxIndexMap<LintId, StableLevelSpec> {
self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty)
}
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
fn insert(&mut self, id: LintId, level_spec: StableLevelSpec) {
self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec);
}
fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> LevelSpec {
fn get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> StableLevelSpec {
self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur)
}
fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) {
fn push_expectation(&mut self, id: Self::LintExpectationId, expectation: LintExpectation) {
self.specs.expectations.push((id, expectation))
}
fn mk_lint_expectation_id(
&self,
_attr_id: AttrId,
attr_index: usize,
lint_index: Option<u16>,
) -> Self::LintExpectationId {
let attr_index = attr_index.try_into().unwrap();
StableLintExpectationId { hir_id: self.cur, attr_index, lint_index }
}
}
impl<'tcx> LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
fn add_id(&mut self, hir_id: HirId) {
self.provider.cur = hir_id;
self.add(
self.provider.attrs.get(hir_id.local_id),
hir_id == hir::CRATE_HIR_ID,
Some(hir_id),
);
self.add(self.provider.attrs.get(hir_id.local_id), hir_id == hir::CRATE_HIR_ID);
}
}
@@ -386,7 +426,7 @@ pub fn crate_root(
crate_attrs: &[ast::Attribute],
) -> Self {
let mut builder = Self::new(sess, features, lint_added_lints, store, registered_tools);
builder.add(crate_attrs, true, None);
builder.add(crate_attrs, true);
builder
}
@@ -413,16 +453,12 @@ fn process_command_line(&mut self) {
/// `#[allow]`
///
/// Don't forget to call `pop`!
pub(crate) fn push(
&mut self,
attrs: &[ast::Attribute],
is_crate_node: bool,
) -> BuilderPush {
pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush {
let prev = self.provider.cur;
self.provider.cur =
self.provider.sets.list.push(LintSet { specs: FxIndexMap::default(), parent: prev });
self.add(attrs, is_crate_node, None);
self.add(attrs, is_crate_node);
if self.provider.current_specs().is_empty() {
self.provider.sets.list.pop();
@@ -446,7 +482,10 @@ fn drop(&mut self) {
}
}
impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P>
where
LevelSpec<P::LintExpectationId>: Into<LevelSpec>,
{
pub(crate) fn sess(&self) -> &Session {
self.sess
}
@@ -455,11 +494,11 @@ pub(crate) fn features(&self) -> &Features {
self.features
}
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec> {
fn current_specs(&self) -> &FxIndexMap<LintId, LevelSpec<P::LintExpectationId>> {
self.provider.current_specs()
}
fn insert(&mut self, id: LintId, level_spec: LevelSpec) {
fn insert(&mut self, id: LintId, level_spec: LevelSpec<P::LintExpectationId>) {
self.provider.insert(id, level_spec)
}
@@ -536,7 +575,7 @@ fn add_command_line(&mut self) {
/// Attempts to insert the `id` to `LevelSpec` 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_spec: LevelSpec) {
fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec<P::LintExpectationId>) {
let level = level_spec.level();
let lint_id = level_spec.lint_id();
let src = level_spec.src;
@@ -561,7 +600,6 @@ fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec) {
// as preventing `allow(lint)` for some lint `lint` in
// `lint_group`. For now, issue a future-compatibility
// warning for this case.
let id_name = id.lint.name_lower();
let fcw_warning = match old_src {
LintLevelSource::Default => false,
LintLevelSource::Node { name, .. } => self.store.is_lint_group(name),
@@ -572,7 +610,7 @@ fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec) {
fcw_warning,
self.current_specs(),
old_src,
id_name
id.lint.name_lower(),
);
let sub = match old_src {
LintLevelSource::Default => {
@@ -637,12 +675,7 @@ fn insert_spec(&mut self, id: LintId, level_spec: LevelSpec) {
};
}
fn add(
&mut self,
attrs: &[impl AttributeExt],
is_crate_node: bool,
source_hir_id: Option<HirId>,
) {
fn add(&mut self, attrs: &[impl AttributeExt], is_crate_node: bool) {
let sess = self.sess;
for (attr_index, attr) in attrs.iter().enumerate() {
if attr.is_automatically_derived_attr() {
@@ -662,23 +695,9 @@ fn add(
continue;
}
let (level, lint_id) = match Level::from_opt_symbol(attr.name()) {
let level = match Level::from_opt_symbol(attr.name()) {
None => continue,
// `Expect` is the only lint level with a `LintExpectationId` that can be created
// from an attribute.
Some(Level::Expect) => {
let id = if let Some(hir_id) = source_hir_id {
LintExpectationId::Stable {
hir_id,
attr_index: attr_index.try_into().unwrap(),
lint_index: None,
}
} else {
LintExpectationId::Unstable { attr_id: attr.id(), lint_index: None }
};
(Level::Expect, Some(id))
}
Some(level) => (level, None),
Some(level) => level,
};
let Some(mut metas) = attr.meta_item_list() else { continue };
@@ -726,10 +745,15 @@ fn add(
}
for (lint_index, li) in metas.iter_mut().enumerate() {
let mut lint_id = lint_id;
if let Some(id) = &mut lint_id {
id.set_lint_index(Some(lint_index as u16));
}
// `Expect` is the only lint level with a `LintExpectationId` that can be created
// from an attribute.
let lint_id = (level == Level::Expect).then(|| {
self.provider.mk_lint_expectation_id(
attr.id(),
attr_index,
Some(lint_index as u16),
)
});
let sp = li.span();
let meta_item = match li {
@@ -987,7 +1011,7 @@ fn into_diag(
}
/// Find the lint level for a lint.
pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec {
pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec<P::LintExpectationId> {
self.provider.get_lint_level_spec(lint, self.sess)
}
+34 -33
View File
@@ -100,47 +100,48 @@ pub enum Applicability {
/// have that amount of lints listed. `u16` values should therefore suffice.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
pub enum LintExpectationId {
/// Used for lints emitted during the `EarlyLintPass`. This id is not
/// hash stable and should not be cached.
Unstable { attr_id: AttrId, lint_index: Option<u16> },
/// The [`HirId`] that the lint expectation is attached to. This id is
/// 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> },
Unstable(UnstableLintExpectationId),
Stable(StableLintExpectationId),
}
impl LintExpectationId {
pub fn is_stable(&self) -> bool {
match self {
LintExpectationId::Unstable { .. } => false,
LintExpectationId::Stable { .. } => true,
}
}
/// Used for lints emitted during the `EarlyLintPass`. This id is not hash
/// stable and should not be cached.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
pub struct UnstableLintExpectationId {
pub attr_id: AttrId,
pub lint_index: Option<u16>,
}
pub fn set_lint_index(&mut self, new_lint_index: Option<u16>) {
let (LintExpectationId::Unstable { lint_index, .. }
| LintExpectationId::Stable { lint_index, .. }) = self;
*lint_index = new_lint_index
impl From<UnstableLintExpectationId> for LintExpectationId {
fn from(id: UnstableLintExpectationId) -> LintExpectationId {
LintExpectationId::Unstable(id)
}
}
impl StableHash for LintExpectationId {
/// The [`HirId`] that the lint expectation is attached to. This id is stable
/// and can be cached. The additional index ensures that nodes with several
/// expectations can correctly match diagnostics to the individual expectation.
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
pub struct StableLintExpectationId {
pub hir_id: HirId,
pub attr_index: u16,
pub lint_index: Option<u16>,
}
impl StableHash for StableLintExpectationId {
#[inline]
fn stable_hash<Hcx: StableHashCtxt>(&self, hcx: &mut Hcx, hasher: &mut StableHasher) {
match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
hir_id.stable_hash(hcx, hasher);
attr_index.stable_hash(hcx, hasher);
lint_index.stable_hash(hcx, hasher);
}
_ => {
unreachable!(
"StableHash should only be called for filled and stable `LintExpectationId`"
)
}
}
let StableLintExpectationId { hir_id, attr_index, lint_index } = self;
hir_id.stable_hash(hcx, hasher);
attr_index.stable_hash(hcx, hasher);
lint_index.expect("must be filled to call `stable_hash`").stable_hash(hcx, hasher);
}
}
impl From<StableLintExpectationId> for LintExpectationId {
fn from(id: StableLintExpectationId) -> LintExpectationId {
LintExpectationId::Stable(id)
}
}
+41 -20
View File
@@ -8,7 +8,8 @@
use rustc_macros::{Decodable, Encodable, StableHash};
use rustc_session::Session;
use rustc_session::lint::{
FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, builtin,
FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, StableLintExpectationId,
UnstableLintExpectationId, builtin,
};
use rustc_span::{DUMMY_SP, ExpnKind, Span, Symbol, kw};
use tracing::instrument;
@@ -55,7 +56,7 @@ pub fn span(&self) -> Span {
/// Convenience helper for things that are frequently used together.
#[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable)]
pub struct LevelSpec {
pub struct LevelSpec<Id = LintExpectationId> {
// This field *must* be private. It must be set in tandem with `lint_id`, only in
// `LevelSpec::new`, because only certain `level`/`lint_id` combinations are valid. See
// `LevelSpec::new` for those combinations.
@@ -68,18 +69,17 @@ pub struct LevelSpec {
level: Level,
// This field *must* be private. See the comment on `level`.
lint_id: Option<LintExpectationId>,
lint_id: Option<Id>,
pub src: LintLevelSource,
}
impl LevelSpec {
pub type UnstableLevelSpec = LevelSpec<UnstableLintExpectationId>;
pub type StableLevelSpec = LevelSpec<StableLintExpectationId>;
impl<Id: Copy> LevelSpec<Id> {
// Panics if an invalid `level`/`lint_id` combination is given.
pub fn new(
level: Level,
lint_id: Option<LintExpectationId>,
src: LintLevelSource,
) -> LevelSpec {
pub fn new(level: Level, lint_id: Option<Id>, src: LintLevelSource) -> LevelSpec<Id> {
match (level, lint_id) {
(Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None) => {}
(Level::Expect, Some(_)) => {}
@@ -101,29 +101,45 @@ pub fn is_expect(self) -> bool {
self.level == Level::Expect
}
pub fn lint_id(self) -> Option<LintExpectationId> {
pub fn lint_id(self) -> Option<Id> {
self.lint_id
}
}
impl From<UnstableLevelSpec> for LevelSpec {
fn from(level: UnstableLevelSpec) -> LevelSpec {
let LevelSpec { level, lint_id, src } = level;
let lint_id = lint_id.map(LintExpectationId::Unstable);
LevelSpec { level, lint_id, src }
}
}
impl From<StableLevelSpec> for LevelSpec {
fn from(level: StableLevelSpec) -> LevelSpec {
let LevelSpec { level, lint_id, src } = level;
let lint_id = lint_id.map(LintExpectationId::Stable);
LevelSpec { level, lint_id, src }
}
}
/// 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, StableHash)]
pub struct ShallowLintLevelMap {
pub expectations: Vec<(LintExpectationId, LintExpectation)>,
pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, LevelSpec>>,
pub expectations: Vec<(StableLintExpectationId, LintExpectation)>,
pub specs: SortedMap<ItemLocalId, FxIndexMap<LintId, StableLevelSpec>>,
}
/// 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_spec(
pub fn reveal_actual_level_spec<Id: Copy>(
sess: &Session,
lint: LintId,
probe_for_lint_level_spec: impl Fn(LintId) -> Option<LevelSpec>,
) -> LevelSpec {
probe_for_lint_level_spec: impl Fn(LintId) -> Option<LevelSpec<Id>>,
) -> LevelSpec<Id> {
let level_spec = probe_for_lint_level_spec(lint);
// If `level` is none then we actually assume the default level for this lint.
@@ -185,7 +201,7 @@ fn probe_for_lint_level_spec(
tcx: TyCtxt<'_>,
id: LintId,
start: HirId,
) -> Option<LevelSpec> {
) -> Option<StableLevelSpec> {
if let Some(map) = self.specs.get(&start.local_id)
&& let Some(level_spec) = map.get(&id)
{
@@ -212,7 +228,12 @@ fn probe_for_lint_level_spec(
/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
#[instrument(level = "trace", skip(self, tcx), ret)]
pub fn lint_level_spec_at_node(&self, tcx: TyCtxt<'_>, lint: LintId, cur: HirId) -> LevelSpec {
pub fn lint_level_spec_at_node(
&self,
tcx: TyCtxt<'_>,
lint: LintId,
cur: HirId,
) -> StableLevelSpec {
reveal_actual_level_spec(tcx.sess, lint, |lint| {
self.probe_for_lint_level_spec(tcx, lint, cur)
})
@@ -221,7 +242,7 @@ pub fn lint_level_spec_at_node(&self, tcx: TyCtxt<'_>, lint: LintId, cur: HirId)
impl TyCtxt<'_> {
/// Fetch and return the user-visible lint level spec for the given lint at the given HirId.
pub fn lint_level_spec_at_node(self, lint: &'static Lint, id: HirId) -> LevelSpec {
pub fn lint_level_spec_at_node(self, lint: &'static Lint, id: HirId) -> StableLevelSpec {
self.shallow_lint_levels_on(id.owner).lint_level_spec_at_node(self, LintId::of(lint), id)
}
}
@@ -362,7 +383,7 @@ fn explain_lint_level_source(
pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>(
sess: &'a Session,
lint: &'static Lint,
level_spec: LevelSpec,
level_spec: impl Into<LevelSpec>,
span: Option<MultiSpan>,
decorate: D,
) {
@@ -556,7 +577,7 @@ fn emit_lint_base_impl<'a>(
emit_lint_base_impl(
sess,
lint,
level_spec,
level_spec.into(),
span,
Box::new(move |dcx, level| decorate.into_diag(dcx, level)),
);
+2 -2
View File
@@ -75,7 +75,7 @@
use rustc_session::cstore::{
CrateDepKind, CrateSource, ExternCrate, ForeignModule, LinkagePreference, NativeLib,
};
use rustc_session::lint::LintExpectationId;
use rustc_session::lint::StableLintExpectationId;
use rustc_span::def_id::LOCAL_CRATE;
use rustc_span::{DUMMY_SP, LocalExpnId, Span, Spanned, Symbol};
use rustc_target::spec::PanicStrategy;
@@ -558,7 +558,7 @@
desc { "looking up lint levels for `{}`", tcx.def_path_str(key) }
}
query lint_expectations(_: ()) -> &'tcx Vec<(LintExpectationId, LintExpectation)> {
query lint_expectations(_: ()) -> &'tcx Vec<(StableLintExpectationId, LintExpectation)> {
arena_cache
desc { "computing `#[expect]`ed lints in this crate" }
}
+7 -4
View File
@@ -22,7 +22,7 @@
use rustc_middle::{bug, span_bug};
use rustc_session::config::CrateType;
use rustc_session::lint::builtin::{DEAD_CODE, DEAD_CODE_PUB_IN_BINARY};
use rustc_session::lint::{self, Lint, LintExpectationId};
use rustc_session::lint::{self, Lint, StableLintExpectationId};
use rustc_span::{Symbol, kw};
use crate::errors::{
@@ -1035,7 +1035,7 @@ fn mark_live_symbols_and_ignored_derived_traits(
struct DeadItem {
def_id: LocalDefId,
name: Symbol,
level_plus: (lint::Level, Option<LintExpectationId>),
level_plus: (lint::Level, Option<StableLintExpectationId>),
}
struct DeadVisitor<'tcx> {
@@ -1082,7 +1082,10 @@ fn should_warn_about_field(&mut self, field: &ty::FieldDef) -> ShouldWarnAboutFi
ShouldWarnAboutField::Yes
}
fn def_lint_level_plus(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
fn def_lint_level_plus(
&self,
id: LocalDefId,
) -> (lint::Level, Option<StableLintExpectationId>) {
let hir_id = self.tcx.local_def_id_to_hir_id(id);
let level_spec = self.tcx.lint_level_spec_at_node(self.target_lint, hir_id);
(level_spec.level(), level_spec.lint_id())
@@ -1267,7 +1270,7 @@ fn warn_multiple(
return;
}
// FIXME: `dead_codes` should probably be morally equivalent to
// `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>`
// `IndexMap<(Level, StableLintExpectationId), (DefId, Symbol)>`
dead_codes.sort_by_key(|v| v.level_plus.0);
for group in dead_codes.chunk_by(|a, b| a.level_plus == b.level_plus) {
self.lint_at_single_level(&group, participle, Some(def_id), report_on);
@@ -2,7 +2,7 @@
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
use rustc_errors::MultiSpan;
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_middle::lint::LevelSpec;
use rustc_middle::lint::UnstableLevelSpec;
use rustc_session::impl_lint_pass;
use rustc_span::{FileName, Span};
use std::collections::BTreeMap;
@@ -51,7 +51,7 @@
struct Modules {
local_path: PathBuf,
spans: Vec<Span>,
lint_level_specs: Vec<LevelSpec>,
lint_level_specs: Vec<UnstableLevelSpec>,
}
#[derive(Default)]
@@ -96,7 +96,7 @@ fn check_crate_post(&mut self, cx: &EarlyContext<'_>, _: &Crate) {
.zip(lint_level_specs)
.filter_map(|(span, level_spec)| {
if let Some(id) = level_spec.lint_id() {
cx.fulfill_expectation(id);
cx.fulfill_expectation(id.into());
}
(!matches!(level_spec.level(), Level::Allow | Level::Expect)).then_some(*span)