From 9bd3f51ac161adee09074419708f450992ad8cc1 Mon Sep 17 00:00:00 2001 From: Amanda Stjerna Date: Thu, 12 Mar 2026 17:17:09 +0100 Subject: [PATCH 1/7] Hand-written Debug implementation for `TypeTest` This adds a hand-written debug format for `TypeTest`s that at least was helpful for me when debugging. It formats an type test using the Unicode turnstile symbol (for "computes") to illustrate that the test encodes a typing rule that, if it holds, produces a conclusion. --- .../rustc_borrowck/src/region_infer/mod.rs | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_borrowck/src/region_infer/mod.rs b/compiler/rustc_borrowck/src/region_infer/mod.rs index 5cdda777723b3..6433019226161 100644 --- a/compiler/rustc_borrowck/src/region_infer/mod.rs +++ b/compiler/rustc_borrowck/src/region_infer/mod.rs @@ -1,4 +1,5 @@ use std::collections::VecDeque; +use std::fmt; use std::rc::Rc; use rustc_data_structures::frozen::Frozen; @@ -182,7 +183,7 @@ pub(crate) enum Cause { /// For more information about this translation, see /// `InferCtxt::process_registered_region_obligations` and /// `InferCtxt::type_must_outlive` in `rustc_infer::infer::InferCtxt`. -#[derive(Clone, Debug)] +#[derive(Clone)] pub(crate) struct TypeTest<'tcx> { /// The type `T` that must outlive the region. pub generic_kind: GenericKind<'tcx>, @@ -198,6 +199,47 @@ pub(crate) struct TypeTest<'tcx> { pub verify_bound: VerifyBound<'tcx>, } +impl fmt::Debug for TypeTest<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fn fmt_bound( + f: &mut fmt::Formatter<'_>, + generic_kind: GenericKind<'_>, + lower: RegionVid, + bound: &VerifyBound<'_>, + ) -> fmt::Result { + let fmt_bounds = + |f: &mut fmt::Formatter<'_>, bounds: &[VerifyBound<'_>]| -> fmt::Result { + let mut it = bounds.iter().peekable(); + while let Some(bound) = it.next() { + fmt_bound(f, generic_kind, lower, bound)?; + if it.peek().is_some() { + write!(f, ", ")? + } + } + Ok(()) + }; + match bound { + VerifyBound::IfEq(binder) => write!(f, "{:?} == {:?}", generic_kind, binder), + VerifyBound::OutlivedBy(region) => write!(f, "{region:?}: {lower:?}"), + VerifyBound::AnyBound(verify_bounds) => { + write!(f, "Any[")?; + fmt_bounds(f, verify_bounds)?; + write!(f, "]") + } + VerifyBound::AllBounds(verify_bounds) => { + write!(f, "All[")?; + fmt_bounds(f, verify_bounds)?; + write!(f, "]") + } + VerifyBound::IsEmpty => write!(f, "Empty({lower:?})"), + } + } + write!(f, "TypeTest from {:?}[", self.span)?; + fmt_bound(f, self.generic_kind, self.lower_bound, &self.verify_bound)?; + write!(f, "] ⊢ {:?}: {:?}", self.generic_kind, self.lower_bound) + } +} + /// When we have an unmet lifetime constraint, we try to propagate it outward (e.g. to a closure /// environment). If we can't, it is an error. #[derive(Clone, Copy, Debug, Eq, PartialEq)] From cef0e9968b4279e9750719a21382b5a170beb619 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Thu, 4 Sep 2025 16:59:04 -0700 Subject: [PATCH 2/7] feat(rustdoc): stabilize `--emit` flag --- src/doc/rustdoc/src/command-line-arguments.md | 31 +++++++++++++++++++ src/librustdoc/lib.rs | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index b55ddf6e0e165..088b453b55b17 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -440,6 +440,37 @@ When `rustdoc` receives this flag, it will print an extra "Version (version)" in the crate root's docs. You can use this flag to differentiate between different versions of your library's documentation. +## `--emit`: control the types of output for rustdoc to emit + +This flag controls the types of output by rustdoc. It accepts a comma-separated +list of values, and may be specified multiple times. The valid emit kinds are: + +- `html-static-files` --- Generates shared static files whose contents are + tied to a specific toolchain version. These are written with a filename + that includes a hash of their contents, so they are safe to cache with + the change if the toolchain version or their contents change, or with the + `Cache-Control: immutable` directive. +- `html-non-static-files` --- Generates files based on the crate(s) being + documented. These file names need to be deterministic so there is no + content-hash in their file names. +- `dep-info` --- Generates a file with Makefile syntax that indicates all the + source files that were loaded to document the crate. The default output + filename is `CRATE_NAME.d`. This emit type can can optionally be followed by + `=` to specify an explicit output path, for example, + `--emit=dep-info=/path/to/foo.d`. The output can be sent to stdout by + specifying `-` as the path (e.g., `--emit=dep-info=-`). + +Using this flag looks like this: + +```bash +$ rustdoc src/lib.rs --emit=html-static-files,html-non-static-files,dep-info=/path/to/build/cache/foo.d +``` + +If not specified, the default emit types would be +`--emit=html-static-files,html-non-static-files`. + +Output files are written to the current directory unless the `--out-dir` flag is used. + ## `-`: load source code from the standard input If you specify `-` as the INPUT on the command line, then `rustdoc` will read the diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 87de4244b5c85..d1d811f89ab34 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -533,7 +533,7 @@ fn opts() -> Vec { "", ), opt( - Unstable, + Stable, Multi, "", "emit", From ccfac5469ac0f4ad858f3f09551e999f1ceb94da Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 12 May 2026 16:24:50 +1000 Subject: [PATCH 3/7] Rename `LevelAndSource` It is misnamed, given that it has three fields: `level`, `lint_id`, and `src`. (Presumably the `lint_id` was added later.) This commit renames it as `LevelSpec`. (I also considered `LevelInfo` but that's very generic. `Spec` has pre-existing uses in `LintLevelsProvider::current_specs` and `LintSet::specs` and `ShallowLintLevelMap::specs`.) Related things renamed as well: - `level` -> `level_spec` (where appropriate) - `lint_levels` -> `lint_level_specs` - `get_lint_level` -> `get_lint_level_spec` - `level_and_source` -> `level_spec` - `CodegenLintLevels` -> `CodegenLintLevelSpecs` - `raw_lint_id_level` -> `raw_lint_level_spec` - `lint_level_at_node` -> `lint_level_spec_at_node` - `reveal_actual_level` -> `reveal_actual_level_spec` - `probe_for_lint_level` -> `probe_for_lint_level_spec` This clears up a lot of `Level` vs. `LevelSpec` ambiguity. E.g. no more `level.level` expressions. --- compiler/rustc_codegen_ssa/src/back/link.rs | 11 +- compiler/rustc_codegen_ssa/src/base.rs | 6 +- compiler/rustc_codegen_ssa/src/lib.rs | 16 +- .../src/const_eval/machine.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 4 +- compiler/rustc_hir_typeck/src/upvar.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 5 +- compiler/rustc_lint/src/context.rs | 14 +- compiler/rustc_lint/src/levels.rs | 139 +++++++++--------- compiler/rustc_lint/src/non_ascii_idents.rs | 9 +- compiler/rustc_metadata/src/creader.rs | 5 +- compiler/rustc_middle/src/lint.rs | 85 ++++++----- compiler/rustc_middle/src/middle/stability.rs | 2 +- compiler/rustc_middle/src/ty/context.rs | 8 +- .../rustc_mir_build/src/check_unsafety.rs | 5 +- .../src/thir/pattern/check_match.rs | 2 +- compiler/rustc_passes/src/dead.rs | 6 +- compiler/rustc_passes/src/stability.rs | 2 +- compiler/rustc_pattern_analysis/src/lints.rs | 10 +- .../passes/calculate_doc_coverage.rs | 6 +- .../passes/check_doc_test_visibility.rs | 4 +- src/tools/clippy/clippy_lints/src/booleans.rs | 4 +- .../src/disallowed_script_idents.rs | 2 +- .../clippy/clippy_lints/src/duplicate_mod.rs | 20 +-- .../src/macro_metavars_in_unsafe.rs | 6 +- .../clippy/clippy_lints/src/module_style.rs | 12 +- .../clippy/clippy_lints/src/raw_strings.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 6 +- 28 files changed, 199 insertions(+), 196 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index dd640d8b1d9d6..c527dd95e2db4 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -60,7 +60,7 @@ use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; -use crate::{CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors}; +use crate::{CodegenLintLevelSpecs, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors}; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -728,7 +728,12 @@ fn is_windows_gnu_clang(sess: &Session) -> bool { && sess.target.options.cfg_abi == CfgAbi::Llvm } -fn report_linker_output(sess: &Session, levels: CodegenLintLevels, stdout: &[u8], stderr: &[u8]) { +fn report_linker_output( + sess: &Session, + levels: CodegenLintLevelSpecs, + stdout: &[u8], + stderr: &[u8], +) { let mut escaped_stderr = escape_string(&stderr); let mut escaped_stdout = escape_string(&stdout); let mut linker_info = String::new(); @@ -1106,7 +1111,7 @@ fn link_natively( } info!("reporting linker output: flavor={flavor:?}"); - report_linker_output(sess, crate_info.lint_levels, &prog.stdout, &prog.stderr); + report_linker_output(sess, crate_info.lint_level_specs, &prog.stdout, &prog.stderr); } Err(e) => { let linker_not_found = e.kind() == io::ErrorKind::NotFound; diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 473a73d43a93a..b0ce3833446d3 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -49,7 +49,9 @@ use crate::meth::load_vtable; use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; -use crate::{CachedModuleCodegen, CodegenLintLevels, CrateInfo, ModuleCodegen, errors, meth, mir}; +use crate::{ + CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, ModuleCodegen, errors, meth, mir, +}; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { match (op, signed) { @@ -953,7 +955,7 @@ impl CrateInfo { dependency_formats: Arc::clone(tcx.dependency_formats(())), windows_subsystem, natvis_debugger_visualizers: Default::default(), - lint_levels: CodegenLintLevels::from_tcx(tcx), + lint_level_specs: CodegenLintLevelSpecs::from_tcx(tcx), metadata_symbol: exported_symbols::metadata_symbol_name(tcx), each_linked_rlib_file_for_lto: Default::default(), exported_symbols_for_lto: Default::default(), diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index e4b61e71a48e7..ec1eb7eeb6fc0 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::LevelAndSource; +use rustc_middle::lint::LevelSpec; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::exported_symbols::SymbolExportKind; @@ -223,7 +223,7 @@ pub struct CrateInfo { pub dependency_formats: Arc, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, - pub lint_levels: CodegenLintLevels, + pub lint_level_specs: CodegenLintLevelSpecs, pub metadata_symbol: String, pub each_linked_rlib_file_for_lto: Vec, pub exported_symbols_for_lto: Vec, @@ -341,16 +341,16 @@ impl CompiledModules { /// solely from the `.rlink` file. `Lint`s are defined too early to be encodeable. /// Instead, encode exactly the information we need. #[derive(Copy, Clone, Debug, Encodable, Decodable)] -pub struct CodegenLintLevels { - linker_messages: LevelAndSource, - linker_info: LevelAndSource, +pub struct CodegenLintLevelSpecs { + linker_messages: LevelSpec, + linker_info: LevelSpec, } -impl CodegenLintLevels { +impl CodegenLintLevelSpecs { pub fn from_tcx(tcx: TyCtxt<'_>) -> Self { Self { - linker_messages: tcx.lint_level_at_node(LINKER_MESSAGES, CRATE_HIR_ID), - linker_info: tcx.lint_level_at_node(LINKER_INFO, CRATE_HIR_ID), + linker_messages: tcx.lint_level_spec_at_node(LINKER_MESSAGES, CRATE_HIR_ID), + linker_info: tcx.lint_level_spec_at_node(LINKER_INFO, CRATE_HIR_ID), } } } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fefbf6adfd132..d83d79aad0dd7 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -735,7 +735,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { let hir_id = ecx.machine.best_lint_scope(*ecx.tcx); let is_error = ecx .tcx - .lint_level_at_node( + .lint_level_spec_at_node( rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL, hir_id, ) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 73de6cf94d5a8..2def1744507ee 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -714,7 +714,7 @@ fn print_crate_info( let lint_store = crate::unerased_lint_store(sess); let features = rustc_expand::config::features(sess, attrs, crate_name); let registered_tools = rustc_resolve::registered_tools_ast(sess.dcx(), attrs, sess); - let lint_levels = rustc_lint::LintLevelsBuilder::crate_root( + let builder = rustc_lint::LintLevelsBuilder::crate_root( sess, &features, true, @@ -729,7 +729,7 @@ fn print_crate_info( // lint is unstable and feature gate isn't active, don't print continue; } - let level = lint_levels.lint_level(lint).level; + let level = builder.lint_level_spec(lint).level; println_info!("{}={}", lint.name_lower(), level.as_str()); } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index db20273f1f654..289857dfd8f52 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -2416,7 +2416,7 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( } let level = tcx - .lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id) + .lint_level_spec_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id) .level; !matches!(level, lint::Level::Allow) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 99a509be03b5b..78b5157983358 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -30,7 +30,6 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId}; use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::{self as hir, Body, FnDecl, ImplItemImplKind, PatKind, PredicateOrigin, find_attr}; use rustc_middle::bug; -use rustc_middle::lint::LevelAndSource; use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{ @@ -695,8 +694,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } // Avoid listing trait impls if the trait is allowed. - let LevelAndSource { level, .. } = - cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()); + let level = + cx.tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).level; if level == Level::Allow { return; } diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 6b0205f9a78a3..4825b61bc6e41 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -20,7 +20,7 @@ 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::LevelAndSource; +use rustc_middle::lint::LevelSpec; 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}; @@ -537,8 +537,8 @@ pub trait LintContext { self.opt_span_lint(lint, Some(span), decorator); } - /// This returns the lint level for the given lint at the current location. - fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource; + /// This returns the lint level spec for the given lint at the current location. + 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, @@ -604,8 +604,8 @@ impl<'tcx> LintContext for LateContext<'tcx> { } } - fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { - self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs) + fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec { + self.tcx.lint_level_spec_at_node(lint, self.last_node_with_lint_attrs) } } @@ -624,8 +624,8 @@ impl LintContext for EarlyContext<'_> { self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorator) } - fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource { - self.builder.lint_level(lint) + fn get_lint_level_spec(&self, lint: &'static Lint) -> LevelSpec { + self.builder.lint_level_spec(lint) } } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a5fe6bdfe6c5d..f647a56b8da3d 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -12,8 +12,8 @@ use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ - LevelAndSource, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base, - reveal_actual_level, + LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base, + reveal_actual_level_spec, }; use rustc_middle::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; @@ -65,9 +65,9 @@ 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 `Level` for which + // -A,-W,-D flags, a `Symbol` for the flag itself and `LevelSpec` for which // flag. - specs: FxIndexMap, + specs: FxIndexMap, parent: LintStackIndex, } @@ -76,32 +76,34 @@ impl LintLevelSets { LintLevelSets { list: IndexVec::new() } } - fn get_lint_level( + fn get_lint_level_spec( &self, lint: &'static Lint, idx: LintStackIndex, - aux: Option<&FxIndexMap>, + aux: Option<&FxIndexMap>, sess: &Session, - ) -> LevelAndSource { - reveal_actual_level(sess, LintId::of(lint), |id| self.raw_lint_id_level(id, idx, aux)) + ) -> LevelSpec { + reveal_actual_level_spec(sess, LintId::of(lint), |id| { + self.raw_lint_level_spec(id, idx, aux) + }) } - fn raw_lint_id_level( + fn raw_lint_level_spec( &self, id: LintId, mut idx: LintStackIndex, - aux: Option<&FxIndexMap>, - ) -> Option { + aux: Option<&FxIndexMap>, + ) -> Option { if let Some(specs) = aux - && let Some(level) = specs.get(&id) + && let Some(level_spec) = specs.get(&id) { - return Some(*level); + return Some(*level_spec); } loop { let LintSet { ref specs, parent } = self.list[idx]; - if let Some(level) = specs.get(&id) { - return Some(*level); + if let Some(level_spec) = specs.get(&id) { + return Some(*level_spec); } if idx == COMMAND_LINE { return None; @@ -125,11 +127,11 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { !has_future_breakage && !lint.eval_always }) .filter(|lint| { - let lint_level = - root_map.lint_level_id_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); + let level_spec = + root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); // Only include lints that are allowed at crate root or by default. - matches!(lint_level.level, Level::Allow) - || (matches!(lint_level.src, LintLevelSource::Default) + matches!(level_spec.level, Level::Allow) + || (matches!(level_spec.src, LintLevelSource::Default) && lint.default_level(tcx.sess.edition()) == Level::Allow) }) .map(|lint| LintId::of(*lint)) @@ -140,8 +142,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { // All lints that appear with a non-allow level must be run. for (_, specs) in map.specs.iter() { - for (lint, level_and_source) in specs.iter() { - if !matches!(level_and_source.level, Level::Allow) { + for (lint, level_spec) in specs.iter() { + if !matches!(level_spec.level, Level::Allow) { dont_need_to_run.remove(lint); } } @@ -212,23 +214,23 @@ pub struct TopDown { } pub trait LintLevelsProvider { - fn current_specs(&self) -> &FxIndexMap; - fn insert(&mut self, id: LintId, lvl: LevelAndSource); - fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource; + 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); } impl LintLevelsProvider for TopDown { - fn current_specs(&self) -> &FxIndexMap { + fn current_specs(&self) -> &FxIndexMap { &self.sets.list[self.cur].specs } - fn insert(&mut self, id: LintId, lvl: LevelAndSource) { - self.sets.list[self.cur].specs.insert(id, lvl); + fn insert(&mut self, id: LintId, level_spec: LevelSpec) { + self.sets.list[self.cur].specs.insert(id, level_spec); } - fn get_lint_level(&self, lint: &'static Lint, sess: &Session) -> LevelAndSource { - self.sets.get_lint_level(lint, self.cur, Some(self.current_specs()), sess) + fn get_lint_level_spec(&self, lint: &'static Lint, sess: &Session) -> LevelSpec { + self.sets.get_lint_level_spec(lint, self.cur, Some(self.current_specs()), sess) } fn push_expectation(&mut self, _: LintExpectationId, _: LintExpectation) {} @@ -239,19 +241,19 @@ 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 { + fn current_specs(&self) -> &FxIndexMap { self.specs.specs.get(&self.cur.local_id).unwrap_or(&self.empty) } - fn insert(&mut self, id: LintId, lvl: LevelAndSource) { - self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, lvl); + fn insert(&mut self, id: LintId, level_spec: LevelSpec) { + self.specs.specs.get_mut_or_insert_default(self.cur.local_id).insert(id, level_spec); } - 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 get_lint_level_spec(&self, lint: &'static Lint, _: &Session) -> LevelSpec { + self.specs.lint_level_spec_at_node(self.tcx, LintId::of(lint), self.cur) } fn push_expectation(&mut self, id: LintExpectationId, expectation: LintExpectation) { self.specs.expectations.push((id, expectation)) @@ -455,12 +457,12 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.features } - fn current_specs(&self) -> &FxIndexMap { + fn current_specs(&self) -> &FxIndexMap { self.provider.current_specs() } - fn insert(&mut self, id: LintId, lvl: LevelAndSource) { - self.provider.insert(id, lvl) + fn insert(&mut self, id: LintId, level_spec: LevelSpec) { + self.provider.insert(id, level_spec) } fn add_command_line(&mut self) { @@ -519,7 +521,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; for &id in ids { // ForceWarn and Forbid cannot be overridden - if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) = + if let Some(LevelSpec { level: Level::ForceWarn | Level::Forbid, .. }) = self.current_specs().get(&id) { continue; @@ -527,18 +529,18 @@ 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, LevelAndSource { level, lint_id: None, src }); + self.insert(id, LevelSpec { level, lint_id: None, src }); } } } } - /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful + /// 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, LevelAndSource { level, lint_id, src }: LevelAndSource) { - let LevelAndSource { level: old_level, src: old_src, .. } = - self.provider.get_lint_level(id.lint, self.sess); + fn insert_spec(&mut self, id: LintId, LevelSpec { level, lint_id, src }: LevelSpec) { + let LevelSpec { level: old_level, src: old_src, .. } = + self.provider.get_lint_level_spec(id.lint, self.sess); // Setting to a non-forbid level is an error if the lint previously had // a forbid level. Note that this is not necessarily true even with a @@ -621,15 +623,14 @@ 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, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src }) + self.insert(id, LevelSpec { level: Level::ForceWarn, lint_id, src: old_src }) } // Keep `ForceWarn` level but drop the expectation - (Level::ForceWarn, _) => self.insert( - id, - LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src }, - ), + (Level::ForceWarn, _) => { + self.insert(id, LevelSpec { level: Level::ForceWarn, lint_id: None, src: old_src }) + } // Set the lint level as normal - _ => self.insert(id, LevelAndSource { level, lint_id, src }), + _ => self.insert(id, LevelSpec { level, lint_id, src }), }; } @@ -644,11 +645,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.is_automatically_derived_attr() { self.insert( LintId::of(SINGLE_USE_LIFETIMES), - LevelAndSource { - level: Level::Allow, - lint_id: None, - src: LintLevelSource::Default, - }, + LevelSpec { level: Level::Allow, lint_id: None, src: LintLevelSource::Default }, ); continue; } @@ -657,11 +654,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.is_doc_hidden() { self.insert( LintId::of(MISSING_DOCS), - LevelAndSource { - level: Level::Allow, - lint_id: None, - src: LintLevelSource::Default, - }, + LevelSpec { level: Level::Allow, lint_id: None, src: LintLevelSource::Default }, ); continue; } @@ -684,7 +677,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { (Level::Expect, Some(stable_id)) } - Some((lvl, id)) => (lvl, id), + Some((level, id)) => (level, id), }; let Some(mut metas) = attr.meta_item_list() else { continue }; @@ -876,7 +869,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let src = LintLevelSource::Node { name, span: sp, reason }; for &id in ids { if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, LevelAndSource { level, lint_id, src }); + self.insert_spec(id, LevelSpec { level, lint_id, src }); } } @@ -907,7 +900,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } if self.lint_added_lints && !is_crate_node { - for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() { + for (id, &LevelSpec { level, ref src, .. }) in self.current_specs().iter() { if !id.lint.crate_level_only { continue; } @@ -975,11 +968,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if self.lint_added_lints { let lint = builtin::UNKNOWN_LINTS; - let level = self.lint_level(builtin::UNKNOWN_LINTS); + let level_spec = self.lint_level_spec(builtin::UNKNOWN_LINTS); emit_lint_base( self.sess, lint, - level, + level_spec, Some(span.into()), UnknownLint { sess: &self.sess, lint_id, feature, lint_from_cli }, ); @@ -989,8 +982,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } /// Find the lint level for a lint. - pub fn lint_level(&self, lint: &'static Lint) -> LevelAndSource { - self.provider.get_lint_level(lint, self.sess) + pub fn lint_level_spec(&self, lint: &'static Lint) -> LevelSpec { + self.provider.get_lint_level_spec(lint, self.sess) } /// Used to emit a lint-related diagnostic based on the current state of @@ -1002,8 +995,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { span: Option, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - let level = self.lint_level(lint); - emit_lint_base(self.sess, lint, level, span, decorator) + let level_spec = self.lint_level_spec(lint); + emit_lint_base(self.sess, lint, level_spec, span, decorator) } #[track_caller] @@ -1013,14 +1006,14 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { span: MultiSpan, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - let level = self.lint_level(lint); - emit_lint_base(self.sess, lint, level, Some(span), decorator); + let level_spec = self.lint_level_spec(lint); + emit_lint_base(self.sess, lint, level_spec, Some(span), decorator); } #[track_caller] pub fn emit_lint(&self, lint: &'static Lint, decorator: impl for<'a> Diagnostic<'a, ()>) { - let level = self.lint_level(lint); - emit_lint_base(self.sess, lint, level, None, decorator); + let level_spec = self.lint_level_spec(lint); + emit_lint_base(self.sess, lint, level_spec, None, decorator); } } diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index 9c11fb41aa6d6..edb0ed779c5bf 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -159,13 +159,14 @@ impl EarlyLintPass for NonAsciiIdents { use rustc_span::Span; use unicode_security::GeneralSecurityProfile; - let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow; + let check_non_ascii_idents = + cx.builder.lint_level_spec(NON_ASCII_IDENTS).level != Level::Allow; let check_uncommon_codepoints = - cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow; + cx.builder.lint_level_spec(UNCOMMON_CODEPOINTS).level != Level::Allow; let check_confusable_idents = - cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow; + cx.builder.lint_level_spec(CONFUSABLE_IDENTS).level != Level::Allow; let check_mixed_script_confusables = - cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow; + cx.builder.lint_level_spec(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow; if !check_non_ascii_idents && !check_uncommon_codepoints diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 9a756337335c0..4dbd0ff725450 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -328,7 +328,10 @@ impl CStore { return; } let level = tcx - .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID) + .lint_level_spec_at_node( + lint::builtin::UNUSED_CRATE_DEPENDENCIES, + rustc_hir::CRATE_HIR_ID, + ) .level; if level != lint::Level::Allow { let unused_externs = diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index a05d24cdabadb..7c53f5b92086b 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -1,4 +1,4 @@ -use std::cmp; +use std::cmp::min; use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::sorted_map::SortedMap; @@ -53,9 +53,9 @@ impl LintLevelSource { } } -/// Convenience helper for moving things around together that frequently are paired +/// Convenience helper for things that are frequently used together. #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable)] -pub struct LevelAndSource { +pub struct LevelSpec { pub level: Level, pub lint_id: Option, pub src: LintLevelSource, @@ -68,21 +68,21 @@ pub struct LevelAndSource { #[derive(Default, Debug, StableHash)] pub struct ShallowLintLevelMap { pub expectations: Vec<(LintExpectationId, LintExpectation)>, - pub specs: SortedMap>, + pub specs: SortedMap>, } /// 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( +pub fn reveal_actual_level_spec( sess: &Session, lint: LintId, - probe_for_lint_level: impl Fn(LintId) -> Option, -) -> LevelAndSource { - let level = probe_for_lint_level(lint); + 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. - let mut level = level.unwrap_or_else(|| LevelAndSource { + let mut level_spec = level_spec.unwrap_or_else(|| LevelSpec { level: lint.lint.default_level(sess.edition()), lint_id: None, src: LintLevelSource::Default, @@ -91,9 +91,11 @@ pub fn reveal_actual_level( // If we're about to issue a warning, check at the last minute for any // directives against the `warnings` lint group. If, for example, there's an // `allow(warnings)` in scope then we want to respect that instead. - if level.level == Level::Warn { - if let Some(configured_level) = probe_for_lint_level(LintId::of(builtin::WARNINGS)) { - let respect_warnings_lint_group = match configured_level.level { + if level_spec.level == Level::Warn { + if let Some(configured_level_spec) = + probe_for_lint_level_spec(LintId::of(builtin::WARNINGS)) + { + let respect_warnings_lint_group = match configured_level_spec.level { // -Wwarnings is a no-op. Level::Warn => false, // Some warnings cannot be denied from the `warnings` lint group, only individually. @@ -105,48 +107,46 @@ pub fn reveal_actual_level( Level::Expect => true, Level::ForceWarn => { sess.dcx().span_delayed_bug( - configured_level.src.span(), + configured_level_spec.src.span(), "cannot --force-warn the `warnings` lint group", ); false } }; if respect_warnings_lint_group { - level = configured_level; + level_spec = configured_level_spec; } } } // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn - level.level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = level.src { - level.level - } else { - cmp::min(level.level, sess.opts.lint_cap.unwrap_or(Level::Forbid)) + if !matches!(level_spec.src, LintLevelSource::CommandLine(_, Level::ForceWarn)) { + level_spec.level = min(level_spec.level, sess.opts.lint_cap.unwrap_or(Level::Forbid)); }; + // Ensure that we never exceed driver level. if let Some(driver_level) = sess.driver_lint_caps.get(&lint) { - // Ensure that we never exceed driver level. - level.level = cmp::min(level.level, *driver_level); + level_spec.level = min(level_spec.level, *driver_level); } - level + level_spec } 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 + /// Perform a deep probe in the HIR tree looking for the actual level spec for the lint. + /// This lint level spec is not usable for diagnostics, it needs to be corrected by /// `reveal_actual_level` beforehand. #[instrument(level = "trace", skip(self, tcx), ret)] - fn probe_for_lint_level( + fn probe_for_lint_level_spec( &self, tcx: TyCtxt<'_>, id: LintId, start: HirId, - ) -> Option { + ) -> Option { if let Some(map) = self.specs.get(&start.local_id) - && let Some(level) = map.get(&id) + && let Some(level_spec) = map.get(&id) { - return Some(*level); + return Some(*level_spec); } let mut owner = start.owner; @@ -158,31 +158,28 @@ impl ShallowLintLevelMap { specs = &tcx.shallow_lint_levels_on(owner).specs; } if let Some(map) = specs.get(&parent.local_id) - && let Some(level) = map.get(&id) + && let Some(level_spec) = map.get(&id) { - return Some(*level); + return Some(*level_spec); } } None } - /// Fetch and return the user-visible lint level for the given lint at the given HirId. + /// 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_id_at_node( - &self, - tcx: TyCtxt<'_>, - lint: LintId, - cur: HirId, - ) -> LevelAndSource { - reveal_actual_level(tcx.sess, lint, |lint| self.probe_for_lint_level(tcx, lint, cur)) + pub fn lint_level_spec_at_node(&self, tcx: TyCtxt<'_>, lint: LintId, cur: HirId) -> LevelSpec { + reveal_actual_level_spec(tcx.sess, lint, |lint| { + self.probe_for_lint_level_spec(tcx, lint, cur) + }) } } 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) -> LevelAndSource { - self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id) + /// 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 { + self.shallow_lint_levels_on(id.owner).lint_level_spec_at_node(self, LintId::of(lint), id) } } @@ -322,7 +319,7 @@ fn explain_lint_level_source( pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( sess: &'a Session, lint: &'static Lint, - level: LevelAndSource, + level_spec: LevelSpec, span: Option, decorate: D, ) { @@ -332,13 +329,13 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( fn emit_lint_base_impl<'a>( sess: &'a Session, lint: &'static Lint, - level: LevelAndSource, + level_spec: LevelSpec, span: Option, decorate: Box< dyn FnOnce(rustc_errors::DiagCtxtHandle<'a>, rustc_errors::Level) -> Diag<'a, ()> + 'a, >, ) { - let LevelAndSource { level, lint_id, src } = level; + let LevelSpec { level, lint_id, src } = level_spec; // Check for future incompatibility lints and issue a stronger warning. let future_incompatible = lint.future_incompatible; @@ -516,7 +513,7 @@ pub fn emit_lint_base<'a, D: Diagnostic<'a, ()> + 'a>( emit_lint_base_impl( sess, lint, - level, + level_spec, span, Box::new(move |dcx, level| decorate.into_diag(dcx, level)), ); diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index 32422aaf83318..c19f51eb9774e 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -234,7 +234,7 @@ fn late_report_deprecation( // Calculating message for lint involves calling `self.def_path_str`, // which will by default invoke the expensive `visible_parent_map` query. // Skip all that work if the lint is allowed anyway. - if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow { + if tcx.lint_level_spec_at_node(lint, hir_id).level == Level::Allow { return; } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 1bf0a562ba49a..e756277b92d76 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2459,8 +2459,8 @@ impl<'tcx> TyCtxt<'tcx> { span: impl Into, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - let level = self.lint_level_at_node(lint, hir_id); - emit_lint_base(self.sess, lint, level, Some(span.into()), decorator) + let level_spec = self.lint_level_spec_at_node(lint, hir_id); + emit_lint_base(self.sess, lint, level_spec, Some(span.into()), decorator) } /// Find the appropriate span where `use` and outer attributes can be inserted at. @@ -2502,8 +2502,8 @@ impl<'tcx> TyCtxt<'tcx> { id: HirId, decorator: impl for<'a> Diagnostic<'a, ()>, ) { - let level = self.lint_level_at_node(lint, id); - emit_lint_base(self.sess, lint, level, None, decorator); + let level_spec = self.lint_level_spec_at_node(lint, id); + emit_lint_base(self.sess, lint, level_spec, None, decorator); } pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate<'tcx>]> { diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 399aee040f40e..193d55e4fdc44 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -175,7 +175,8 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool { - self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level == Level::Allow + self.tcx.lint_level_spec_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level + == Level::Allow } /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body. @@ -234,7 +235,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { } BlockSafety::ExplicitUnsafe(hir_id) => { let used = matches!( - self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level, + self.tcx.lint_level_spec_at_node(UNUSED_UNSAFE, hir_id).level, Level::Allow ); self.in_safety_context( diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index 395eaf55265c0..ce63d9054cc0c 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1026,7 +1026,7 @@ fn find_fallback_pattern_typo<'tcx>( pat: &Pat<'tcx>, lint: &mut UnreachablePattern<'_>, ) { - if let Level::Allow = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id).level { + if let Level::Allow = cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).level { // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd // ICE. At the same time, we don't really need to do all of this if we won't emit anything. return; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 2ee094c87616c..426608af0cc97 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -779,7 +779,7 @@ fn has_allow_dead_code_or_lang_attr( ) -> Option { fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir_id = tcx.local_def_id_to_hir_id(def_id); - let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level; + let lint_level = tcx.lint_level_spec_at_node(lint::builtin::DEAD_CODE, hir_id).level; matches!(lint_level, lint::Allow | lint::Expect) } @@ -1084,8 +1084,8 @@ impl<'tcx> DeadVisitor<'tcx> { fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option) { let hir_id = self.tcx.local_def_id_to_hir_id(id); - let level = self.tcx.lint_level_at_node(self.target_lint, hir_id); - (level.level, level.lint_id) + let level_spec = self.tcx.lint_level_spec_at_node(self.target_lint, hir_id); + (level_spec.level, level_spec.lint_id) } fn dead_code_pub_in_binary_note(&self) -> Option { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 420e74b130598..d68eccf0a31fe 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -843,7 +843,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // Calculating message for lint involves calling `self.def_path_str`, // which will by default invoke the expensive `visible_parent_map` query. // Skip all that work if the lint is allowed anyway. - if self.tcx.lint_level_at_node(DEPRECATED, id).level + if self.tcx.lint_level_spec_at_node(DEPRECATED, id).level == lint::Level::Allow { return; diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index 5a8adf4841c40..cb2a05a6e902e 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,4 +1,4 @@ -use rustc_middle::lint::LevelAndSource; +use rustc_middle::lint::LevelSpec; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::ErrorGuaranteed; use tracing::instrument; @@ -65,7 +65,9 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( scrut_ty: RevealedTy<'tcx>, ) -> Result<(), ErrorGuaranteed> { if !matches!( - rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level, + rcx.tcx + .lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level) + .level, rustc_session::lint::Level::Allow ) { let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?; @@ -89,8 +91,8 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( // arm. This no longer makes sense so we warn users, to avoid silently breaking their // usage of the lint. for arm in arms { - let LevelAndSource { level, src, .. } = - rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); + let LevelSpec { level, src, .. } = + rcx.tcx.lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); if !matches!(level, rustc_session::lint::Level::Allow) { rcx.tcx.dcx().emit_warn(NonExhaustiveOmittedPatternLintOnArm { span: arm.pat.data().span, diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index ac5e7805005eb..4f67c4adea529 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -5,7 +5,7 @@ use std::ops; use rustc_hir as hir; use rustc_lint::builtin::MISSING_DOCS; -use rustc_middle::lint::{LevelAndSource, LintLevelSource}; +use rustc_middle::lint::{LevelSpec, LintLevelSource}; use rustc_session::lint; use rustc_span::{FileName, RemapPathScopeComponents}; use serde::Serialize; @@ -222,8 +222,8 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); - let LevelAndSource { level, src, .. } = - self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id); + let LevelSpec { level, src, .. } = + self.ctx.tcx.lint_level_spec_at_node(MISSING_DOCS, hir_id); // In case we have: // diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 89caa9399f2bd..2a20768f3abaf 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -7,7 +7,7 @@ use rustc_hir as hir; use rustc_macros::Diagnostic; -use rustc_middle::lint::{LevelAndSource, LintLevelSource}; +use rustc_middle::lint::{LevelSpec, LintLevelSource}; use rustc_session::lint; use tracing::debug; @@ -110,7 +110,7 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - { return false; } - let LevelAndSource { level, src, .. } = cx.tcx.lint_level_at_node( + let LevelSpec { level, src, .. } = cx.tcx.lint_level_spec_at_node( crate::lint::MISSING_DOC_CODE_EXAMPLES, cx.tcx.local_def_id_to_hir_id(def_id), ); diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index ed963dc3e90e7..c7dab109d6e36 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -204,7 +204,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) { && !expr.span.from_expansion() && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(cx, msrv, inner) - && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow + && cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow { use clippy_utils::sugg::{Sugg, has_enclosing_paren}; let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { @@ -611,7 +611,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { } } let nonminimal_bool_lint = |mut suggestions: Vec<_>| { - if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow { + if self.cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow { suggestions.sort(); span_lint_hir_and_then( self.cx, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 4596d9457c0b7..69a9b473a6474 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents { // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint: // https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_lint/src/non_ascii_idents.rs - let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow; + let check_disallowed_script_idents = cx.builder.lint_level_spec(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow; if !check_disallowed_script_idents { return; } diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index f3facac321073..d0cb180c1e3db 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::LevelAndSource; +use rustc_middle::lint::LevelSpec; 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_levels: Vec, + lint_level_specs: Vec, } #[derive(Default)] @@ -71,10 +71,10 @@ impl EarlyLintPass for DuplicateMod { let modules = self.modules.entry(absolute_path).or_insert(Modules { local_path, spans: Vec::new(), - lint_levels: Vec::new(), + lint_level_specs: Vec::new(), }); modules.spans.push(item.span_with_attributes()); - modules.lint_levels.push(cx.get_lint_level(DUPLICATE_MOD)); + modules.lint_level_specs.push(cx.get_lint_level_spec(DUPLICATE_MOD)); } } @@ -82,7 +82,7 @@ impl EarlyLintPass for DuplicateMod { for Modules { local_path, spans, - lint_levels, + lint_level_specs, } in self.modules.values() { if spans.len() < 2 { @@ -90,16 +90,16 @@ impl EarlyLintPass for DuplicateMod { } // At this point the lint would be emitted - assert_eq!(spans.len(), lint_levels.len()); + assert_eq!(spans.len(), lint_level_specs.len()); let spans: Vec<_> = spans .iter() - .zip(lint_levels) - .filter_map(|(span, lvl)| { - if let Some(id) = lvl.lint_id { + .zip(lint_level_specs) + .filter_map(|(span, level_spec)| { + if let Some(id) = level_spec.lint_id { cx.fulfill_expectation(id); } - (!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span) + (!matches!(level_spec.level, Level::Allow | Level::Expect)).then_some(*span) }) .collect(); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index a7eda8e2932af..30ff41d0689a0 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -6,7 +6,7 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource, find_attr}; use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::lint::LevelAndSource; +use rustc_middle::lint::LevelSpec; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; @@ -252,11 +252,11 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { .flatten() .copied() .inspect(|&unsafe_block| { - if let LevelAndSource { + if let LevelSpec { level: Level::Expect, lint_id: Some(id), .. - } = cx.tcx.lint_level_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) + } = cx.tcx.lint_level_spec_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) { // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index c1c5a01427197..2694e87aa4468 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -137,9 +137,9 @@ impl EarlyLintPass for ModStyle { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level(INLINE_MODULES).level == Level::Allow + if cx.builder.lint_level_spec(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level_spec(INLINE_MODULES).level == Level::Allow { return; } @@ -192,9 +192,9 @@ impl EarlyLintPass for ModStyle { } fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level(INLINE_MODULES).level == Level::Allow + if cx.builder.lint_level_spec(MOD_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).level == Level::Allow + && cx.builder.lint_level_spec(INLINE_MODULES).level == Level::Allow { return; } diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index 7f08fe0daca43..d95aecc061a2b 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -132,7 +132,7 @@ impl RawStrings { ); }, ); - if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) { + if !matches!(cx.get_lint_level_spec(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) { return; } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 522d35bd6093e..5dc340e3828bc 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -104,7 +104,7 @@ use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::lint::LevelAndSource; +use rustc_middle::lint::LevelSpec; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; @@ -1660,7 +1660,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I let mut suppress_lint = false; for id in ids { - let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id); + let LevelSpec { level, lint_id, .. } = cx.tcx.lint_level_spec_at_node(lint, id); if let Some(expectation) = lint_id { cx.fulfill_expectation(expectation); } @@ -1682,7 +1682,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that /// expectations at the checked nodes will be fulfilled. pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool { - cx.tcx.lint_level_at_node(lint, id).level == Level::Allow + cx.tcx.lint_level_spec_at_node(lint, id).level == Level::Allow } pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { From f9fd7f76f2e41c92b66b0f2b51f4053419d5b3bb Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 May 2026 10:21:38 +1000 Subject: [PATCH 4/7] Rename `DeadItem::level` The name is misleading because the field contains a `Level` *and* an `Option`. This commit renames it `level_plus` which better communicates that it's more than just a level. --- compiler/rustc_passes/src/dead.rs | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 426608af0cc97..265ddeb88e5f6 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -1035,7 +1035,7 @@ fn mark_live_symbols_and_ignored_derived_traits( struct DeadItem { def_id: LocalDefId, name: Symbol, - level: (lint::Level, Option), + level_plus: (lint::Level, Option), } struct DeadVisitor<'tcx> { @@ -1082,7 +1082,7 @@ impl<'tcx> DeadVisitor<'tcx> { ShouldWarnAboutField::Yes } - fn def_lint_level(&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) @@ -1108,8 +1108,8 @@ impl<'tcx> DeadVisitor<'tcx> { let Some(&first_item) = dead_codes.first() else { return }; let tcx = self.tcx; - let first_lint_level = first_item.level; - assert!(dead_codes.iter().skip(1).all(|item| item.level == first_lint_level)); + let first_lint_level_plus = first_item.level_plus; + assert!(dead_codes.iter().skip(1).all(|item| item.level_plus == first_lint_level_plus)); let names: Vec<_> = dead_codes.iter().map(|item| item.name).collect(); let spans: Vec<_> = dead_codes @@ -1266,9 +1266,10 @@ impl<'tcx> DeadVisitor<'tcx> { if dead_codes.is_empty() { return; } - // FIXME: `dead_codes` should probably be morally equivalent to `IndexMap<(Level, LintExpectationId), (DefId, Symbol)>` - dead_codes.sort_by_key(|v| v.level.0); - for group in dead_codes.chunk_by(|a, b| a.level == b.level) { + // FIXME: `dead_codes` should probably be morally equivalent to + // `IndexMap<(Level, LintExpectationId), (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); } } @@ -1277,7 +1278,7 @@ impl<'tcx> DeadVisitor<'tcx> { let item = DeadItem { def_id: id, name: self.tcx.item_name(id.to_def_id()), - level: self.def_lint_level(id), + level_plus: self.def_lint_level_plus(id), }; self.lint_at_single_level(&[&item], participle, None, ReportOn::NamedField); } @@ -1381,8 +1382,8 @@ fn lint_dead_codes<'tcx>( && !visitor.is_live_code(local_def_id) { let name = tcx.item_name(def_id); - let level = visitor.def_lint_level(local_def_id); - dead_codes.push(DeadItem { def_id: local_def_id, name, level }); + let level_plus = visitor.def_lint_level_plus(local_def_id); + dead_codes.push(DeadItem { def_id: local_def_id, name, level_plus }); } } } @@ -1408,8 +1409,8 @@ fn lint_dead_codes<'tcx>( let def_id = variant.def_id.expect_local(); if !live_symbols.contains(&def_id) { // Record to group diagnostics. - let level = visitor.def_lint_level(def_id); - dead_variants.push(DeadItem { def_id, name: variant.name, level }); + let level_plus = visitor.def_lint_level_plus(def_id); + dead_variants.push(DeadItem { def_id, name: variant.name, level_plus }); continue; } @@ -1424,8 +1425,8 @@ fn lint_dead_codes<'tcx>( .filter_map(|field| { let def_id = field.did.expect_local(); if let ShouldWarnAboutField::Yes = visitor.should_warn_about_field(field) { - let level = visitor.def_lint_level(def_id); - Some(DeadItem { def_id, name: field.name, level }) + let level_plus = visitor.def_lint_level_plus(def_id); + Some(DeadItem { def_id, name: field.name, level_plus }) } else { None } From f439907325a2ad9577069324875b475b2a3aa13c Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 May 2026 14:24:16 +1000 Subject: [PATCH 5/7] Streamline `Level::from_{attr,symbol}` These methods return `Option<(Self, Option)>`. But all the call sites except one don't look at the `Option`. This commit simplifies these methods to not return the `Option`. This means they no longer need to be passed a closure to compute an `AttrId` (which is usually discarded anyway). The commit also renames `from_attr` as `from_opt_symbol`, because it takes an `Option`, not an `Attribute`. These changes simplify all the call sites that don't need the `Option`, and also the one call site that does (in `LintLevelsBuilder::add`): that call site no longer needs to do an awkward destructuring, and can instead build the appropriate `LintExpectationId` directly. --- compiler/rustc_lint/src/levels.rs | 31 ++++++++--------- compiler/rustc_lint_defs/src/lib.rs | 33 +++++-------------- compiler/rustc_mir_build/src/builder/scope.rs | 2 +- .../clippy/clippy_lints/src/attrs/mod.rs | 2 +- .../src/attrs/unnecessary_clippy_cfg.rs | 2 +- .../src/attrs/useless_attribute.rs | 2 +- .../clippy/clippy_lints/src/attrs/utils.rs | 6 ++-- .../clippy/clippy_lints/src/collapsible_if.rs | 2 +- .../src/returns/needless_return.rs | 2 +- 9 files changed, 32 insertions(+), 50 deletions(-) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f647a56b8da3d..afe244993b0a8 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -9,7 +9,6 @@ use rustc_hir as hir; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; use rustc_index::IndexVec; -use rustc_middle::bug; use rustc_middle::hir::nested_filter; use rustc_middle::lint::{ LevelSpec, LintExpectation, LintLevelSource, ShallowLintLevelMap, emit_lint_base, @@ -659,25 +658,23 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { continue; } - let (level, lint_id) = match Level::from_attr(attr.name(), || attr.id()) { + let (level, lint_id) = match Level::from_opt_symbol(attr.name()) { None => continue, - // This is the only lint level with a `LintExpectationId` that can be created from - // an attribute. - Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => { - let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = 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: None, + // `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(stable_id)) + (Level::Expect, Some(id)) } - Some((level, id)) => (level, id), + Some(level) => (level, None), }; let Some(mut metas) = attr.meta_item_list() else { continue }; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 1e08adca88e28..9fe45290f0368 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -215,34 +215,19 @@ impl Level { } } - /// Converts an `Attribute` to a level. - pub fn from_attr( - attr_name: Option, - attr_id: impl Fn() -> AttrId, - ) -> Option<(Self, Option)> { - attr_name.and_then(|name| Self::from_symbol(name, || Some(attr_id()))) + /// Converts an `Option` to a level. + pub fn from_opt_symbol(s: Option) -> Option { + s.and_then(Self::from_symbol) } /// Converts a `Symbol` to a level. - pub fn from_symbol( - s: Symbol, - id: impl FnOnce() -> Option, - ) -> Option<(Self, Option)> { + pub fn from_symbol(s: Symbol) -> Option { match s { - sym::allow => Some((Level::Allow, None)), - sym::expect => { - if let Some(attr_id) = id() { - Some(( - Level::Expect, - Some(LintExpectationId::Unstable { attr_id, lint_index: None }), - )) - } else { - None - } - } - sym::warn => Some((Level::Warn, None)), - sym::deny => Some((Level::Deny, None)), - sym::forbid => Some((Level::Forbid, None)), + sym::allow => Some(Level::Allow), + sym::expect => Some(Level::Expect), + sym::warn => Some(Level::Warn), + sym::deny => Some(Level::Deny), + sym::forbid => Some(Level::Forbid), _ => None, } } diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 26cc1394be50c..b0a0c97acf535 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -1302,7 +1302,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { .tcx .hir_attrs(id) .iter() - .any(|attr| Level::from_attr(attr.name(), || attr.id()).is_some()) + .any(|attr| Level::from_opt_symbol(attr.name()).is_some()) { // This is a rare case. It's for a node path that doesn't reach the root due to an // intervening lint level attribute. This result doesn't get cached. diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs index c15a378053e39..372defbb4d7e2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs @@ -583,7 +583,7 @@ impl EarlyLintPass for PostExpansionEarlyAttributes { if matches!(name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { allow_attributes_without_reason::check(cx, name, items, attr); } - if is_lint_level(name, attr.id) { + if is_lint_level(name) { blanket_clippy_restriction_lints::check(cx, name, items); } if items.is_empty() || !attr.has_name(sym::deprecated) { diff --git a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs index 6ee3290fa761d..6efc931df2429 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs @@ -15,7 +15,7 @@ pub(super) fn check( ) { if cfg_attr.has_name(sym::clippy) && let Some(ident) = behind_cfg_attr.ident() - && Level::from_symbol(ident.name, || Some(attr.id)).is_some() + && Level::from_symbol(ident.name).is_some() && let Some(items) = behind_cfg_attr.meta_item_list() { let nb_items = items.len(); diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs index 9a1e315ae5306..c025b47dc787d 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs @@ -15,7 +15,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) { return; } if let Some(lint_list) = &attr.meta_item_list() - && attr.name().is_some_and(|name| is_lint_level(name, attr.id)) + && attr.name().is_some_and(|name| is_lint_level(name)) { for lint in lint_list { match item.kind { diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs index 7b66f91f6c073..4822bdcb9bde2 100644 --- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs +++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs @@ -1,5 +1,5 @@ use clippy_utils::macros::{is_panic, macro_backtrace}; -use rustc_ast::{AttrId, MetaItemInner}; +use rustc_ast::MetaItemInner; use rustc_hir::{ Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind, }; @@ -16,8 +16,8 @@ pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool { } } -pub(super) fn is_lint_level(symbol: Symbol, attr_id: AttrId) -> bool { - Level::from_symbol(symbol, || Some(attr_id)).is_some() +pub(super) fn is_lint_level(symbol: Symbol) -> bool { + Level::from_symbol(symbol).is_some() } pub(super) fn is_relevant_item(cx: &LateContext<'_>, item: &Item<'_>) -> bool { diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs index 8a12d1093b4b4..3b9e0171f587d 100644 --- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs +++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs @@ -238,7 +238,7 @@ impl CollapsibleIf { }, [attr] - if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _))) + if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) && let Some(metas) = attr.meta_item_list() && let Some(MetaItemInner::MetaItem(meta_item)) = metas.first() && let [tool, lint_name] = meta_item.path.segments.as_slice() diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs index b9bacc2b73a17..f3d6324e1885e 100644 --- a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs +++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs @@ -181,7 +181,7 @@ fn check_final_expr<'tcx>( match cx.tcx.hir_attrs(expr.hir_id) { [] => {}, [attr] => { - if matches!(Level::from_attr(attr.name(), || attr.id()), Some((Level::Expect, _))) + if matches!(Level::from_opt_symbol(attr.name()), Some(Level::Expect)) && let metas = attr.meta_item_list() && let Some(lst) = metas && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() From 530a73f17e52664ea155776068f2fd592e365087 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 May 2026 10:35:58 +1000 Subject: [PATCH 6/7] Prevent invalid `LevelSpec` values `LevelSpec` has two related fields, `level` and `lint_id`. This commit makes the fields private (and introduces getters) to ensure they are set together using only valid combinations. The commit also introduces `is_allow` and `is_expect` methods because those are useful. --- .../src/const_eval/machine.rs | 2 +- compiler/rustc_driver_impl/src/lib.rs | 2 +- compiler/rustc_hir_typeck/src/upvar.rs | 7 +-- compiler/rustc_lint/src/builtin.rs | 7 +-- compiler/rustc_lint/src/levels.rs | 43 +++++++++------ compiler/rustc_lint/src/non_ascii_idents.rs | 12 ++-- compiler/rustc_metadata/src/creader.rs | 2 +- compiler/rustc_middle/src/lint.rs | 55 +++++++++++++++++-- compiler/rustc_middle/src/middle/stability.rs | 4 +- .../rustc_mir_build/src/check_unsafety.rs | 9 +-- .../src/thir/pattern/check_match.rs | 3 +- compiler/rustc_passes/src/dead.rs | 4 +- compiler/rustc_passes/src/stability.rs | 4 +- compiler/rustc_pattern_analysis/src/lints.rs | 19 +++---- .../passes/calculate_doc_coverage.rs | 9 ++- .../passes/check_doc_test_visibility.rs | 7 +-- src/tools/clippy/clippy_lints/src/booleans.rs | 6 +- .../src/disallowed_script_idents.rs | 4 +- .../clippy/clippy_lints/src/duplicate_mod.rs | 4 +- .../src/macro_metavars_in_unsafe.rs | 15 ++--- .../clippy/clippy_lints/src/module_style.rs | 14 ++--- .../clippy/clippy_lints/src/raw_strings.rs | 2 +- src/tools/clippy/clippy_utils/src/lib.rs | 9 ++- 23 files changed, 136 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index d83d79aad0dd7..fb456d80e465f 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -739,7 +739,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL, hir_id, ) - .level + .level() .is_error(); let span = ecx.cur_span(); ecx.tcx.emit_node_span_lint( diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 2def1744507ee..31353d1eac0fb 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -729,7 +729,7 @@ fn print_crate_info( // lint is unstable and feature gate isn't active, don't print continue; } - let level = builder.lint_level_spec(lint).level; + let level = builder.lint_level_spec(lint).level(); println_info!("{}={}", lint.name_lower(), level.as_str()); } } diff --git a/compiler/rustc_hir_typeck/src/upvar.rs b/compiler/rustc_hir_typeck/src/upvar.rs index 289857dfd8f52..be9c562031698 100644 --- a/compiler/rustc_hir_typeck/src/upvar.rs +++ b/compiler/rustc_hir_typeck/src/upvar.rs @@ -2415,11 +2415,8 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis( return false; } - let level = tcx - .lint_level_spec_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id) - .level; - - !matches!(level, lint::Level::Allow) + !tcx.lint_level_spec_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id) + .is_allow() } /// Return a two string tuple (s1, s2) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 78b5157983358..672bbdece2861 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -60,7 +60,8 @@ use crate::lints::{ BuiltinUnreachablePub, BuiltinUnsafe, BuiltinUnstableFeatures, BuiltinUnusedDocComment, BuiltinUnusedDocCommentSub, BuiltinWhileTrue, EqInternalMethodImplemented, InvalidAsmLabel, }; -use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext}; +use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext}; + declare_lint! { /// The `while_true` lint detects `while true { }`. /// @@ -694,9 +695,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { } // Avoid listing trait impls if the trait is allowed. - let level = - cx.tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).level; - if level == Level::Allow { + if cx.tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).is_allow() { return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index afe244993b0a8..ad74b0ee906a1 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -129,7 +129,7 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { let level_spec = root_map.lint_level_spec_at_node(tcx, LintId::of(lint), hir::CRATE_HIR_ID); // Only include lints that are allowed at crate root or by default. - matches!(level_spec.level, Level::Allow) + level_spec.is_allow() || (matches!(level_spec.src, LintLevelSource::Default) && lint.default_level(tcx.sess.edition()) == Level::Allow) }) @@ -142,7 +142,7 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> UnordSet { // All lints that appear with a non-allow level must be run. for (_, specs) in map.specs.iter() { for (lint, level_spec) in specs.iter() { - if !matches!(level_spec.level, Level::Allow) { + if !level_spec.is_allow() { dont_need_to_run.remove(lint); } } @@ -520,15 +520,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { }; for &id in ids { // ForceWarn and Forbid cannot be overridden - if let Some(LevelSpec { level: Level::ForceWarn | Level::Forbid, .. }) = - self.current_specs().get(&id) + if let Some(level_spec) = self.current_specs().get(&id) + && matches!(level_spec.level(), Level::ForceWarn | Level::Forbid) { continue; } if self.check_gated_lint(id, DUMMY_SP, true) { let src = LintLevelSource::CommandLine(lint_flag_val, level); - self.insert(id, LevelSpec { level, lint_id: None, src }); + self.insert(id, LevelSpec::new(level, None, src)); } } } @@ -537,9 +537,14 @@ 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, LevelSpec { level, lint_id, src }: LevelSpec) { - let LevelSpec { level: old_level, src: old_src, .. } = - self.provider.get_lint_level_spec(id.lint, self.sess); + 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; + + let old_level_spec = self.provider.get_lint_level_spec(id.lint, self.sess); + let old_level = old_level_spec.level(); + let old_src = old_level_spec.src; // 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 @@ -622,14 +627,14 @@ 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 { level: Level::ForceWarn, lint_id, src: old_src }) + self.insert(id, LevelSpec::new(Level::ForceWarn, lint_id, old_src)) } // Keep `ForceWarn` level but drop the expectation (Level::ForceWarn, _) => { - self.insert(id, LevelSpec { level: Level::ForceWarn, lint_id: None, src: old_src }) + self.insert(id, LevelSpec::new(Level::ForceWarn, None, old_src)) } // Set the lint level as normal - _ => self.insert(id, LevelSpec { level, lint_id, src }), + _ => self.insert(id, LevelSpec::new(level, lint_id, src)), }; } @@ -644,7 +649,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.is_automatically_derived_attr() { self.insert( LintId::of(SINGLE_USE_LIFETIMES), - LevelSpec { level: Level::Allow, lint_id: None, src: LintLevelSource::Default }, + LevelSpec::new(Level::Allow, None, LintLevelSource::Default), ); continue; } @@ -653,7 +658,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { if attr.is_doc_hidden() { self.insert( LintId::of(MISSING_DOCS), - LevelSpec { level: Level::Allow, lint_id: None, src: LintLevelSource::Default }, + LevelSpec::new(Level::Allow, None, LintLevelSource::Default), ); continue; } @@ -866,7 +871,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { let src = LintLevelSource::Node { name, span: sp, reason }; for &id in ids { if self.check_gated_lint(id, sp, false) { - self.insert_spec(id, LevelSpec { level, lint_id, src }); + self.insert_spec(id, LevelSpec::new(level, lint_id, src)); } } @@ -897,12 +902,13 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } if self.lint_added_lints && !is_crate_node { - for (id, &LevelSpec { level, ref src, .. }) in self.current_specs().iter() { + for (id, level_spec) in self.current_specs().iter() { if !id.lint.crate_level_only { continue; } - let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = *src + let LintLevelSource::Node { name: lint_attr_name, span: lint_attr_span, .. } = + level_spec.src else { continue; }; @@ -910,7 +916,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { self.emit_span_lint( UNUSED_ATTRIBUTES, lint_attr_span.into(), - IgnoredUnlessCrateSpecified { level: level.as_str(), name: lint_attr_name }, + IgnoredUnlessCrateSpecified { + level: level_spec.level().as_str(), + name: lint_attr_name, + }, ); // don't set a separate error for every lint in the group break; diff --git a/compiler/rustc_lint/src/non_ascii_idents.rs b/compiler/rustc_lint/src/non_ascii_idents.rs index edb0ed779c5bf..42aba3ffcfe24 100644 --- a/compiler/rustc_lint/src/non_ascii_idents.rs +++ b/compiler/rustc_lint/src/non_ascii_idents.rs @@ -155,18 +155,14 @@ impl EarlyLintPass for NonAsciiIdents { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { use std::collections::BTreeMap; - use rustc_session::lint::Level; use rustc_span::Span; use unicode_security::GeneralSecurityProfile; - let check_non_ascii_idents = - cx.builder.lint_level_spec(NON_ASCII_IDENTS).level != Level::Allow; - let check_uncommon_codepoints = - cx.builder.lint_level_spec(UNCOMMON_CODEPOINTS).level != Level::Allow; - let check_confusable_idents = - cx.builder.lint_level_spec(CONFUSABLE_IDENTS).level != Level::Allow; + let check_non_ascii_idents = !cx.builder.lint_level_spec(NON_ASCII_IDENTS).is_allow(); + let check_uncommon_codepoints = !cx.builder.lint_level_spec(UNCOMMON_CODEPOINTS).is_allow(); + let check_confusable_idents = !cx.builder.lint_level_spec(CONFUSABLE_IDENTS).is_allow(); let check_mixed_script_confusables = - cx.builder.lint_level_spec(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow; + !cx.builder.lint_level_spec(MIXED_SCRIPT_CONFUSABLES).is_allow(); if !check_non_ascii_idents && !check_uncommon_codepoints diff --git a/compiler/rustc_metadata/src/creader.rs b/compiler/rustc_metadata/src/creader.rs index 4dbd0ff725450..3082af3f0e805 100644 --- a/compiler/rustc_metadata/src/creader.rs +++ b/compiler/rustc_metadata/src/creader.rs @@ -332,7 +332,7 @@ impl CStore { lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID, ) - .level; + .level(); if level != lint::Level::Allow { let unused_externs = self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::>(); diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 7c53f5b92086b..41eeb1fc9811f 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -56,11 +56,56 @@ impl LintLevelSource { /// Convenience helper for things that are frequently used together. #[derive(Copy, Clone, Debug, StableHash, Encodable, Decodable)] pub struct LevelSpec { - pub level: Level, - pub lint_id: Option, + // 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. + // + // If you are thinking right now that `level` and `lint_id` should be combined into a single + // type that excludes the invalid combinations, that's a reasonable thought, but in practice + // it's painful because `level` needs to be used by itself, without `lint_id`, in many places. + // Making the fields private prevents invalid combinations while retaining the flexibility of + // two separate fields. + level: Level, + + // This field *must* be private. See the comment on `level`. + lint_id: Option, + pub src: LintLevelSource, } +impl LevelSpec { + // Panics if an invalid `level`/`lint_id` combination is given. + 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(_)) => {} + (Level::ForceWarn, _) => {} + _ => panic!("invalid level/lint_id combination"), + } + LevelSpec { level, lint_id, src } + } + + pub fn level(self) -> Level { + self.level + } + + pub fn is_allow(self) -> bool { + self.level == Level::Allow + } + + pub fn is_expect(self) -> bool { + self.level == Level::Expect + } + + pub fn lint_id(self) -> Option { + self.lint_id + } +} + /// Return type for the `shallow_lint_levels_on` query. /// /// This map represents the set of allowed lints and allowance levels given @@ -82,10 +127,8 @@ pub fn reveal_actual_level_spec( let level_spec = probe_for_lint_level_spec(lint); // If `level` is none then we actually assume the default level for this lint. - let mut level_spec = level_spec.unwrap_or_else(|| LevelSpec { - level: lint.lint.default_level(sess.edition()), - lint_id: None, - src: LintLevelSource::Default, + let mut level_spec = level_spec.unwrap_or_else(|| { + LevelSpec::new(lint.lint.default_level(sess.edition()), None, LintLevelSource::Default) }); // If we're about to issue a warning, check at the last minute for any diff --git a/compiler/rustc_middle/src/middle/stability.rs b/compiler/rustc_middle/src/middle/stability.rs index c19f51eb9774e..db91a889bec4b 100644 --- a/compiler/rustc_middle/src/middle/stability.rs +++ b/compiler/rustc_middle/src/middle/stability.rs @@ -13,7 +13,7 @@ use rustc_macros::{Decodable, Encodable, StableHash, Subdiagnostic}; use rustc_session::Session; use rustc_session::errors::feature_err_issue; use rustc_session::lint::builtin::{DEPRECATED, DEPRECATED_IN_FUTURE}; -use rustc_session::lint::{DeprecatedSinceKind, Level, Lint}; +use rustc_session::lint::{DeprecatedSinceKind, Lint}; use rustc_span::{Span, Symbol, sym}; use tracing::debug; @@ -234,7 +234,7 @@ fn late_report_deprecation( // Calculating message for lint involves calling `self.def_path_str`, // which will by default invoke the expensive `visible_parent_map` query. // Skip all that work if the lint is allowed anyway. - if tcx.lint_level_spec_at_node(lint, hir_id).level == Level::Allow { + if tcx.lint_level_spec_at_node(lint, hir_id).is_allow() { return; } diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs index 193d55e4fdc44..d546b79148198 100644 --- a/compiler/rustc_mir_build/src/check_unsafety.rs +++ b/compiler/rustc_mir_build/src/check_unsafety.rs @@ -12,7 +12,6 @@ use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_session::lint::Level; use rustc_session::lint::builtin::{DEPRECATED_SAFE_2024, UNSAFE_OP_IN_UNSAFE_FN, UNUSED_UNSAFE}; use rustc_span::def_id::{DefId, LocalDefId}; use rustc_span::{Span, Symbol}; @@ -175,8 +174,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> { /// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool { - self.tcx.lint_level_spec_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).level - == Level::Allow + self.tcx.lint_level_spec_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).is_allow() } /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body. @@ -234,10 +232,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> { }); } BlockSafety::ExplicitUnsafe(hir_id) => { - let used = matches!( - self.tcx.lint_level_spec_at_node(UNUSED_UNSAFE, hir_id).level, - Level::Allow - ); + let used = self.tcx.lint_level_spec_at_node(UNUSED_UNSAFE, hir_id).is_allow(); self.in_safety_context( SafetyContext::UnsafeBlock { span: block.span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index ce63d9054cc0c..7469f3d14f06b 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -8,7 +8,6 @@ use rustc_hir::def::*; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, MatchSource}; use rustc_infer::infer::TyCtxtInferExt; -use rustc_lint_defs::Level; use rustc_middle::bug; use rustc_middle::thir::visit::Visitor; use rustc_middle::thir::*; @@ -1026,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>( pat: &Pat<'tcx>, lint: &mut UnreachablePattern<'_>, ) { - if let Level::Allow = cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).level { + if cx.tcx.lint_level_spec_at_node(UNREACHABLE_PATTERNS, hir_id).is_allow() { // This is because we use `with_no_trimmed_paths` later, so if we never emit the lint we'd // ICE. At the same time, we don't really need to do all of this if we won't emit anything. return; diff --git a/compiler/rustc_passes/src/dead.rs b/compiler/rustc_passes/src/dead.rs index 265ddeb88e5f6..5d3ea7e2c237c 100644 --- a/compiler/rustc_passes/src/dead.rs +++ b/compiler/rustc_passes/src/dead.rs @@ -779,7 +779,7 @@ fn has_allow_dead_code_or_lang_attr( ) -> Option { fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { let hir_id = tcx.local_def_id_to_hir_id(def_id); - let lint_level = tcx.lint_level_spec_at_node(lint::builtin::DEAD_CODE, hir_id).level; + let lint_level = tcx.lint_level_spec_at_node(lint::builtin::DEAD_CODE, hir_id).level(); matches!(lint_level, lint::Allow | lint::Expect) } @@ -1085,7 +1085,7 @@ impl<'tcx> DeadVisitor<'tcx> { 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) + (level_spec.level(), level_spec.lint_id()) } fn dead_code_pub_in_binary_note(&self) -> Option { diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index d68eccf0a31fe..84eba2ace19ae 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -843,9 +843,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> { // Calculating message for lint involves calling `self.def_path_str`, // which will by default invoke the expensive `visible_parent_map` query. // Skip all that work if the lint is allowed anyway. - if self.tcx.lint_level_spec_at_node(DEPRECATED, id).level - == lint::Level::Allow - { + if self.tcx.lint_level_spec_at_node(DEPRECATED, id).is_allow() { return; } // Show a deprecation message. diff --git a/compiler/rustc_pattern_analysis/src/lints.rs b/compiler/rustc_pattern_analysis/src/lints.rs index cb2a05a6e902e..d258733dc095a 100644 --- a/compiler/rustc_pattern_analysis/src/lints.rs +++ b/compiler/rustc_pattern_analysis/src/lints.rs @@ -1,4 +1,3 @@ -use rustc_middle::lint::LevelSpec; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_span::ErrorGuaranteed; use tracing::instrument; @@ -64,12 +63,11 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( pat_column: &PatternColumn<'p, RustcPatCtxt<'p, 'tcx>>, scrut_ty: RevealedTy<'tcx>, ) -> Result<(), ErrorGuaranteed> { - if !matches!( - rcx.tcx - .lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level) - .level, - rustc_session::lint::Level::Allow - ) { + if !rcx + .tcx + .lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level) + .is_allow() + { let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?; if !witnesses.is_empty() { // Report that a match of a `non_exhaustive` enum marked with `non_exhaustive_omitted_patterns` @@ -91,12 +89,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>( // arm. This no longer makes sense so we warn users, to avoid silently breaking their // usage of the lint. for arm in arms { - let LevelSpec { level, src, .. } = + let level_spec = rcx.tcx.lint_level_spec_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); - if !matches!(level, rustc_session::lint::Level::Allow) { + let level = level_spec.level(); + if level != rustc_session::lint::Level::Allow { rcx.tcx.dcx().emit_warn(NonExhaustiveOmittedPatternLintOnArm { span: arm.pat.data().span, - lint_span: src.span(), + lint_span: level_spec.src.span(), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), lint_level: level.as_str(), lint_name: "non_exhaustive_omitted_patterns", diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 4f67c4adea529..200a5a71b453a 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -5,8 +5,7 @@ use std::ops; use rustc_hir as hir; use rustc_lint::builtin::MISSING_DOCS; -use rustc_middle::lint::{LevelSpec, LintLevelSource}; -use rustc_session::lint; +use rustc_middle::lint::LintLevelSource; use rustc_span::{FileName, RemapPathScopeComponents}; use serde::Serialize; use tracing::debug; @@ -222,8 +221,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { let has_doc_example = tests.found_tests != 0; let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); - let LevelSpec { level, src, .. } = - self.ctx.tcx.lint_level_spec_at_node(MISSING_DOCS, hir_id); + let level_spec = self.ctx.tcx.lint_level_spec_at_node(MISSING_DOCS, hir_id); // In case we have: // @@ -258,7 +256,8 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> { // unless the user had an explicit `allow`. // let should_have_docs = !should_be_ignored - && (level != lint::Level::Allow || matches!(src, LintLevelSource::Default)); + && (!level_spec.is_allow() + || matches!(level_spec.src, LintLevelSource::Default)); if let Some(span) = i.span(self.ctx.tcx) { let filename = span.filename(self.ctx.sess()); diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs index 2a20768f3abaf..a8a9a42df4575 100644 --- a/src/librustdoc/passes/check_doc_test_visibility.rs +++ b/src/librustdoc/passes/check_doc_test_visibility.rs @@ -7,8 +7,7 @@ use rustc_hir as hir; use rustc_macros::Diagnostic; -use rustc_middle::lint::{LevelSpec, LintLevelSource}; -use rustc_session::lint; +use rustc_middle::lint::LintLevelSource; use tracing::debug; use super::Pass; @@ -110,11 +109,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) - { return false; } - let LevelSpec { level, src, .. } = cx.tcx.lint_level_spec_at_node( + let level_spec = cx.tcx.lint_level_spec_at_node( crate::lint::MISSING_DOC_CODE_EXAMPLES, cx.tcx.local_def_id_to_hir_id(def_id), ); - level != lint::Level::Allow || matches!(src, LintLevelSource::Default) + !level_spec.is_allow() || matches!(level_spec.src, LintLevelSource::Default) } pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs index c7dab109d6e36..ef741c9f4d9e1 100644 --- a/src/tools/clippy/clippy_lints/src/booleans.rs +++ b/src/tools/clippy/clippy_lints/src/booleans.rs @@ -11,7 +11,7 @@ use rustc_ast::ast::LitKind; use rustc_errors::Applicability; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{BinOpKind, Body, Expr, ExprKind, FnDecl, RustcVersion, UnOp}; -use rustc_lint::{LateContext, LateLintPass, Level}; +use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, Symbol, SyntaxContext}; @@ -204,7 +204,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) { && !expr.span.from_expansion() && !inner.span.from_expansion() && let Some(suggestion) = simplify_not(cx, msrv, inner) - && cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow + && !cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, expr.hir_id).is_allow() { use clippy_utils::sugg::{Sugg, has_enclosing_paren}; let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { @@ -611,7 +611,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> { } } let nonminimal_bool_lint = |mut suggestions: Vec<_>| { - if self.cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow { + if !self.cx.tcx.lint_level_spec_at_node(NONMINIMAL_BOOL, e.hir_id).is_allow() { suggestions.sort(); span_lint_hir_and_then( self.cx, diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs index 69a9b473a6474..d013e453ecc0a 100644 --- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs +++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs @@ -2,7 +2,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint; use rustc_ast::ast; use rustc_data_structures::fx::FxHashSet; -use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use unicode_script::{Script, UnicodeScript}; @@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents { // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint: // https://github.com/rust-lang/rust/blob/HEAD/compiler/rustc_lint/src/non_ascii_idents.rs - let check_disallowed_script_idents = cx.builder.lint_level_spec(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow; + let check_disallowed_script_idents = !cx.builder.lint_level_spec(DISALLOWED_SCRIPT_IDENTS).is_allow(); if !check_disallowed_script_idents { return; } diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs index d0cb180c1e3db..cb3cb2479beed 100644 --- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs +++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs @@ -95,11 +95,11 @@ impl EarlyLintPass for DuplicateMod { .iter() .zip(lint_level_specs) .filter_map(|(span, level_spec)| { - if let Some(id) = level_spec.lint_id { + if let Some(id) = level_spec.lint_id() { cx.fulfill_expectation(id); } - (!matches!(level_spec.level, Level::Allow | Level::Expect)).then_some(*span) + (!matches!(level_spec.level(), Level::Allow | Level::Expect)).then_some(*span) }) .collect(); diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs index 30ff41d0689a0..5f086988c083b 100644 --- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs +++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs @@ -5,8 +5,7 @@ use itertools::Itertools; use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt}; use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource, find_attr}; -use rustc_lint::{LateContext, LateLintPass, Level, LintContext}; -use rustc_middle::lint::LevelSpec; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext}; use std::collections::BTreeMap; @@ -252,17 +251,15 @@ impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe { .flatten() .copied() .inspect(|&unsafe_block| { - if let LevelSpec { - level: Level::Expect, - lint_id: Some(id), - .. - } = cx.tcx.lint_level_spec_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block) - { + let level_spec = + cx.tcx.lint_level_spec_at_node(MACRO_METAVARS_IN_UNSAFE, unsafe_block); + if level_spec.is_expect() { // Since we're going to deduplicate expanded unsafe blocks by its enclosing macro definition soon, // which would lead to unfulfilled `#[expect()]`s in all other unsafe blocks that are filtered out // except for the one we emit the warning at, we must manually fulfill the lint // for all unsafe blocks here. - cx.fulfill_expectation(id); + // `unwrap` is safe because `Expect` lints always have a `lint_id`. + cx.fulfill_expectation(level_spec.lint_id().unwrap()); } }) .map(|id| { diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs index 2694e87aa4468..9058968866e52 100644 --- a/src/tools/clippy/clippy_lints/src/module_style.rs +++ b/src/tools/clippy/clippy_lints/src/module_style.rs @@ -2,7 +2,7 @@ use clippy_utils::ast_utils::is_cfg_test; use clippy_utils::diagnostics::span_lint_and_then; use clippy_utils::source::snippet; use rustc_ast::ast::{self, Inline, ItemKind, ModKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LOCAL_CRATE; use rustc_span::{FileName, Ident, SourceFile, Span, SyntaxContext, sym}; @@ -137,9 +137,9 @@ impl EarlyLintPass for ModStyle { } fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if cx.builder.lint_level_spec(MOD_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level_spec(INLINE_MODULES).level == Level::Allow + if cx.builder.lint_level_spec(MOD_MODULE_FILES).is_allow() + && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).is_allow() + && cx.builder.lint_level_spec(INLINE_MODULES).is_allow() { return; } @@ -192,9 +192,9 @@ impl EarlyLintPass for ModStyle { } fn check_item_post(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) { - if cx.builder.lint_level_spec(MOD_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).level == Level::Allow - && cx.builder.lint_level_spec(INLINE_MODULES).level == Level::Allow + if cx.builder.lint_level_spec(MOD_MODULE_FILES).is_allow() + && cx.builder.lint_level_spec(SELF_NAMED_MODULE_FILES).is_allow() + && cx.builder.lint_level_spec(INLINE_MODULES).is_allow() { return; } diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs index d95aecc061a2b..d8aa88d48b09d 100644 --- a/src/tools/clippy/clippy_lints/src/raw_strings.rs +++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs @@ -132,7 +132,7 @@ impl RawStrings { ); }, ); - if !matches!(cx.get_lint_level_spec(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) { + if !cx.get_lint_level_spec(NEEDLESS_RAW_STRINGS).is_allow() { return; } } diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 5dc340e3828bc..f4c96163aa605 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -104,7 +104,6 @@ use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; -use rustc_middle::lint::LevelSpec; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; @@ -1660,12 +1659,12 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I let mut suppress_lint = false; for id in ids { - let LevelSpec { level, lint_id, .. } = cx.tcx.lint_level_spec_at_node(lint, id); - if let Some(expectation) = lint_id { + let level_spec = cx.tcx.lint_level_spec_at_node(lint, id); + if let Some(expectation) = level_spec.lint_id() { cx.fulfill_expectation(expectation); } - match level { + match level_spec.level() { Level::Allow | Level::Expect => suppress_lint = true, Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {}, } @@ -1682,7 +1681,7 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that /// expectations at the checked nodes will be fulfilled. pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool { - cx.tcx.lint_level_spec_at_node(lint, id).level == Level::Allow + cx.tcx.lint_level_spec_at_node(lint, id).is_allow() } pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { From bc127a6a4bc6c78e37d06caa0a129f7d7b3589ca Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 14 May 2026 14:15:55 +1000 Subject: [PATCH 7/7] Remove unused `LintExpectationId::get_lint_index`. --- compiler/rustc_lint_defs/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 9fe45290f0368..2e690c7185f8c 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -118,13 +118,6 @@ impl LintExpectationId { } } - pub fn get_lint_index(&self) -> Option { - let (LintExpectationId::Unstable { lint_index, .. } - | LintExpectationId::Stable { lint_index, .. }) = self; - - *lint_index - } - pub fn set_lint_index(&mut self, new_lint_index: Option) { let (LintExpectationId::Unstable { lint_index, .. } | LintExpectationId::Stable { lint_index, .. }) = self;