diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index ec1eb7eeb6fc0..fc57310804873 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -26,7 +26,7 @@ use rustc_lint_defs::builtin::LINKER_INFO; 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 @@ impl CompiledModules { /// 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 { diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 4825b61bc6e41..3cbd02e2ed8d9 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -20,14 +20,17 @@ use rustc_hir::def_id::{CrateNum, DefId}; 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; + fn sess(&self) -> &Session; // FIXME: These methods should not take an Into -- instead, callers should need to @@ -538,7 +543,7 @@ pub trait LintContext { } /// 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; /// 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 @@ pub trait LintContext { /// 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 @@ pub trait LintContext { .dcx() .struct_expect( "this is a dummy diagnostic, to submit and store an expectation", - expectation, + expectation.into(), ) .emit(); } @@ -585,6 +590,8 @@ impl<'a> EarlyContext<'a> { } 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 @@ impl<'tcx> LintContext for LateContext<'tcx> { } } - 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 @@ impl LintContext for EarlyContext<'_> { 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) } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 903e223e97938..df6683c14df42 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -53,7 +53,7 @@ impl<'ecx, T: EarlyLintPass> EarlyContextAndPass<'ecx, T> { { 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); debug!("early context: enter_attrs({:?})", attrs); lint_callback!(self, check_attributes, attrs); diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 481e116d06e01..5364a4141805b 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -2,8 +2,8 @@ use rustc_data_structures::fx::FxHashSet; 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) { // 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) { 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 }, ); diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 3cc0d46d8541f..0a074c6652a1d 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -439,14 +439,6 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { filtered_passes.push(Box::new(HardwiredLints)); let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; - late_lint_crate_inner(tcx, context, pass); -} - -fn late_lint_crate_inner<'tcx, T: LateLintPass<'tcx>>( - tcx: TyCtxt<'tcx>, - context: LateContext<'tcx>, - pass: T, -) { let mut cx = LateContextAndPass { context, pass }; // Visit the whole crate. diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index ad74b0ee906a1..b8da46594cfc2 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -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_hir::intravisit::{self, Visitor}; 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 @@ use rustc_session::lint::builtin::{ 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 @@ rustc_index::newtype_index! { /// 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, + specs: FxIndexMap, parent: LintStackIndex, } @@ -79,9 +81,9 @@ impl LintLevelSets { &self, lint: &'static Lint, idx: LintStackIndex, - aux: Option<&FxIndexMap>, + aux: Option<&FxIndexMap>, 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 @@ impl LintLevelSets { &self, id: LintId, mut idx: LintStackIndex, - aux: Option<&FxIndexMap>, - ) -> Option { + aux: Option<&FxIndexMap>, + ) -> Option { 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; - 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; + + fn current_specs(&self) -> &FxIndexMap>; + + 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: Self::LintExpectationId, expectation: LintExpectation); + + fn mk_lint_expectation_id( + &self, + attr_id: AttrId, + attr_index: usize, + lint_index: Option, + ) -> Self::LintExpectationId; } impl LintLevelsProvider for TopDown { - fn current_specs(&self) -> &FxIndexMap { + type LintExpectationId = UnstableLintExpectationId; + + fn current_specs(&self) -> &FxIndexMap { &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, + ) -> 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, + empty: FxIndexMap, attrs: &'tcx hir::AttributeMap<'tcx>, } impl LintLevelsProvider for LintLevelQueryMap<'_> { - fn current_specs(&self) -> &FxIndexMap { + type LintExpectationId = StableLintExpectationId; + + fn current_specs(&self) -> &FxIndexMap { 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, + ) -> 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 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { 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,17 +453,12 @@ impl<'s> LintLevelsBuilder<'s, TopDown> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub(crate) fn push( - &mut self, - attrs: &[ast::Attribute], - is_crate_node: bool, - source_hir_id: Option, - ) -> 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, source_hir_id); + self.add(attrs, is_crate_node); if self.provider.current_specs().is_empty() { self.provider.sets.list.pop(); @@ -447,7 +482,10 @@ impl Drop for BuilderPush { } } -impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { +impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> +where + LevelSpec: Into, +{ pub(crate) fn sess(&self) -> &Session { self.sess } @@ -456,14 +494,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.features } - fn current_specs(&self) -> &FxIndexMap { - self.provider.current_specs() - } - - fn insert(&mut self, id: LintId, level_spec: LevelSpec) { - self.provider.insert(id, level_spec) - } - fn add_command_line(&mut self) { for &(ref lint_name, level) in &self.sess.opts.lint_opts { // Checks the validity of lint names derived from the command line. @@ -520,7 +550,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; for &id in ids { // ForceWarn and Forbid cannot be overridden - if let Some(level_spec) = self.current_specs().get(&id) + if let Some(level_spec) = self.provider.current_specs().get(&id) && matches!(level_spec.level(), Level::ForceWarn | Level::Forbid) { continue; @@ -528,7 +558,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if self.check_gated_lint(id, DUMMY_SP, true) { let src = LintLevelSource::CommandLine(lint_flag_val, level); - self.insert(id, LevelSpec::new(level, None, src)); + self.provider.insert(id, LevelSpec::new(level, None, src)); } } } @@ -537,7 +567,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { /// 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) { let level = level_spec.level(); let lint_id = level_spec.lint_id(); let src = level_spec.src; @@ -562,7 +592,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // 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), @@ -571,9 +600,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { debug!( "fcw_warning={:?}, specs.get(&id) = {:?}, old_src={:?}, id_name={:?}", fcw_warning, - self.current_specs(), + self.provider.current_specs(), old_src, - id_name + id.lint.name_lower(), ); let sub = match old_src { LintLevelSource::Default => { @@ -627,27 +656,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { match (old_level, level) { // If the new level is an expectation store it in `ForceWarn` (Level::ForceWarn, Level::Expect) => { - self.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src)) + self.provider.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src)) } // Keep `ForceWarn` level but drop the expectation (Level::ForceWarn, _) => { - self.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src)) + self.provider.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src)) } // Set the lint level as normal - _ => self.insert(id, LevelSpec::new(level, lint_id, src)), + _ => self.provider.insert(id, LevelSpec::new(level, lint_id, src)), }; } - fn add( - &mut self, - attrs: &[impl AttributeExt], - is_crate_node: bool, - source_hir_id: Option, - ) { + 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() { - self.insert( + self.provider.insert( LintId::of(SINGLE_USE_LIFETIMES), LevelSpec::new(Level::Allow, None, LintLevelSource::Default), ); @@ -656,30 +680,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { // `#[doc(hidden)]` disables missing_docs check. if attr.is_doc_hidden() { - self.insert( + self.provider.insert( LintId::of(MISSING_DOCS), LevelSpec::new(Level::Allow, None, LintLevelSource::Default), ); 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 }; @@ -727,10 +737,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } 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 { @@ -902,7 +917,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } if self.lint_added_lints && !is_crate_node { - for (id, level_spec) in self.current_specs().iter() { + for (id, level_spec) in self.provider.current_specs().iter() { if !id.lint.crate_level_only { continue; } @@ -988,7 +1003,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } /// 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 { self.provider.get_lint_level_spec(lint, self.sess) } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2e690c7185f8c..fc8b2b65a8d48 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -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 }, - /// 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 }, -} - -impl LintExpectationId { - pub fn is_stable(&self) -> bool { - match self { - LintExpectationId::Unstable { .. } => false, - LintExpectationId::Stable { .. } => true, - } - } + Unstable(UnstableLintExpectationId), + Stable(StableLintExpectationId), +} - pub fn set_lint_index(&mut self, new_lint_index: Option) { - let (LintExpectationId::Unstable { lint_index, .. } - | LintExpectationId::Stable { lint_index, .. }) = self; +/// 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, +} - *lint_index = new_lint_index +impl From 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, +} + +impl StableHash for StableLintExpectationId { #[inline] fn stable_hash(&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 for LintExpectationId { + fn from(id: StableLintExpectationId) -> LintExpectationId { + LintExpectationId::Stable(id) } } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 41eeb1fc9811f..aa4b8f1d12c9f 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -8,7 +8,8 @@ use rustc_lint_defs::EditionFcw; 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 @@ impl LintLevelSource { /// Convenience helper for things that are frequently used together. #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable)] -pub struct LevelSpec { +pub struct LevelSpec { // 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, + lint_id: Option, pub src: LintLevelSource, } -impl LevelSpec { +pub type UnstableLevelSpec = LevelSpec; +pub type StableLevelSpec = LevelSpec; + +impl LevelSpec { // Panics if an invalid `level`/`lint_id` combination is given. - pub fn new( - level: Level, - lint_id: Option, - src: LintLevelSource, - ) -> LevelSpec { + pub fn new(level: Level, lint_id: Option, src: LintLevelSource) -> LevelSpec { match (level, lint_id) { (Level::Allow | Level::Warn | Level::Deny | Level::Forbid, None) => {} (Level::Expect, Some(_)) => {} @@ -101,29 +101,48 @@ impl LevelSpec { self.level == Level::Expect } - pub fn lint_id(self) -> Option { + pub fn lint_id(self) -> Option { self.lint_id } } +impl From 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 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*. +/// This map represents lints levels given by the attributes for *a single HirId*. #[derive(Default, Debug, StableHash)] pub struct ShallowLintLevelMap { - pub expectations: Vec<(LintExpectationId, LintExpectation)>, - pub specs: SortedMap>, + // All the specs for this HirId. This is accessed frequently, e.g. for every lint emitted. + pub specs: SortedMap>, + + // Additional information about the `expect` specs for this HirId. This is consulted only once + // per compilation session, in `check_expectations`/`lint_expectations`. + pub expectations: Vec<(StableLintExpectationId, LintExpectation)>, } /// 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( sess: &Session, lint: LintId, - probe_for_lint_level_spec: impl Fn(LintId) -> Option, -) -> LevelSpec { + probe_for_lint_level_spec: impl Fn(LintId) -> Option>, +) -> LevelSpec { 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 +204,7 @@ impl ShallowLintLevelMap { tcx: TyCtxt<'_>, id: LintId, start: HirId, - ) -> Option { + ) -> Option { if let Some(map) = self.specs.get(&start.local_id) && let Some(level_spec) = map.get(&id) { @@ -212,7 +231,12 @@ impl ShallowLintLevelMap { /// 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 +245,7 @@ impl ShallowLintLevelMap { 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 +386,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, span: Option, decorate: D, ) { @@ -556,7 +580,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( emit_lint_base_impl( sess, lint, - level_spec, + level_spec.into(), span, Box::new(move |dcx, level| decorate.into_diag(dcx, level)), ); diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index a9fc5dcac8f29..d62e4ec941d21 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -75,7 +75,7 @@ use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolMangli 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 @@ rustc_queries! { 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" } } diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 5d3ea7e2c237c..92a0f14f3e94e 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -22,7 +22,7 @@ use rustc_middle::ty::{self, AssocTag, TyCtxt}; 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), + level_plus: (lint::Level, Option), } struct DeadVisitor<'tcx> { @@ -1082,7 +1082,10 @@ impl<'tcx> DeadVisitor<'tcx> { ShouldWarnAboutField::Yes } - fn def_lint_level_plus(&self, id: LocalDefId) -> (lint::Level, Option) { + fn def_lint_level_plus( + &self, + id: LocalDefId, + ) -> (lint::Level, Option) { 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 @@ impl<'tcx> DeadVisitor<'tcx> { 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); diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index cb3cb2479beed..f1837e776e824 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help; 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 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]); struct Modules { local_path: PathBuf, spans: Vec, - lint_level_specs: Vec, + lint_level_specs: Vec, } #[derive(Default)] @@ -96,7 +96,7 @@ impl EarlyLintPass for DuplicateMod { .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)