1
Fork 0

Rollup merge of #138610 - oli-obk:no-sort-hir-ids, r=compiler-errors

impl !PartialOrd for HirId

revive of https://github.com/rust-lang/rust/pull/92233

Another checkbox of https://github.com/rust-lang/rust/issues/90317, another small step in making incremental less likely to die in horrible ways
This commit is contained in:
Matthias Krüger 2025-04-03 21:18:30 +02:00 committed by GitHub
commit 48a3919884
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
39 changed files with 295 additions and 240 deletions

View file

@ -959,9 +959,9 @@ fn link_natively(
} }
} }
let (level, src) = codegen_results.crate_info.lint_levels.linker_messages; let level = codegen_results.crate_info.lint_levels.linker_messages;
let lint = |msg| { let lint = |msg| {
lint_level(sess, LINKER_MESSAGES, level, src, None, |diag| { lint_level(sess, LINKER_MESSAGES, level, None, |diag| {
LinkerOutput { inner: msg }.decorate_lint(diag) LinkerOutput { inner: msg }.decorate_lint(diag)
}) })
}; };

View file

@ -34,7 +34,7 @@ use rustc_hir::CRATE_HIR_ID;
use rustc_hir::def_id::CrateNum; use rustc_hir::def_id::CrateNum;
use rustc_macros::{Decodable, Encodable, HashStable}; use rustc_macros::{Decodable, Encodable, HashStable};
use rustc_middle::dep_graph::WorkProduct; use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::lint::LintLevelSource; use rustc_middle::lint::LevelAndSource;
use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile;
use rustc_middle::middle::dependency_format::Dependencies; use rustc_middle::middle::dependency_format::Dependencies;
use rustc_middle::middle::exported_symbols::SymbolExportKind; use rustc_middle::middle::exported_symbols::SymbolExportKind;
@ -45,7 +45,6 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
use rustc_session::Session; use rustc_session::Session;
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
use rustc_session::cstore::{self, CrateSource}; use rustc_session::cstore::{self, CrateSource};
use rustc_session::lint::Level;
use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_session::lint::builtin::LINKER_MESSAGES;
use rustc_session::utils::NativeLibKind; use rustc_session::utils::NativeLibKind;
use rustc_span::Symbol; use rustc_span::Symbol;
@ -341,7 +340,7 @@ impl CodegenResults {
/// Instead, encode exactly the information we need. /// Instead, encode exactly the information we need.
#[derive(Copy, Clone, Debug, Encodable, Decodable)] #[derive(Copy, Clone, Debug, Encodable, Decodable)]
pub struct CodegenLintLevels { pub struct CodegenLintLevels {
linker_messages: (Level, LintLevelSource), linker_messages: LevelAndSource,
} }
impl CodegenLintLevels { impl CodegenLintLevels {

View file

@ -546,7 +546,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL, rustc_session::lint::builtin::LONG_RUNNING_CONST_EVAL,
hir_id, hir_id,
) )
.0 .level
.is_error(); .is_error();
let span = ecx.cur_span(); let span = ecx.cur_span();
ecx.tcx.emit_node_span_lint( ecx.tcx.emit_node_span_lint(

View file

@ -715,7 +715,7 @@ fn print_crate_info(
// lint is unstable and feature gate isn't active, don't print // lint is unstable and feature gate isn't active, don't print
continue; continue;
} }
let level = lint_levels.lint_level(lint).0; let level = lint_levels.lint_level(lint).level;
println_info!("{}={}", lint.name_lower(), level.as_str()); println_info!("{}={}", lint.name_lower(), level.as_str());
} }
} }

View file

@ -91,13 +91,13 @@ fn annotation_level_for_level(level: Level) -> annotate_snippets::Level {
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => { Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => {
annotate_snippets::Level::Error annotate_snippets::Level::Error
} }
Level::ForceWarning(_) | Level::Warning => annotate_snippets::Level::Warning, Level::ForceWarning | Level::Warning => annotate_snippets::Level::Warning,
Level::Note | Level::OnceNote => annotate_snippets::Level::Note, Level::Note | Level::OnceNote => annotate_snippets::Level::Note,
Level::Help | Level::OnceHelp => annotate_snippets::Level::Help, Level::Help | Level::OnceHelp => annotate_snippets::Level::Help,
// FIXME(#59346): Not sure how to map this level // FIXME(#59346): Not sure how to map this level
Level::FailureNote => annotate_snippets::Level::Error, Level::FailureNote => annotate_snippets::Level::Error,
Level::Allow => panic!("Should not call with Allow"), Level::Allow => panic!("Should not call with Allow"),
Level::Expect(_) => panic!("Should not call with Expect"), Level::Expect => panic!("Should not call with Expect"),
} }
} }

View file

@ -9,7 +9,7 @@ use std::thread::panicking;
use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::FxIndexMap;
use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and}; use rustc_error_messages::{FluentValue, fluent_value_from_str_list_sep_by_and};
use rustc_lint_defs::Applicability; use rustc_lint_defs::{Applicability, LintExpectationId};
use rustc_macros::{Decodable, Encodable}; use rustc_macros::{Decodable, Encodable};
use rustc_span::source_map::Spanned; use rustc_span::source_map::Spanned;
use rustc_span::{DUMMY_SP, Span, Symbol}; use rustc_span::{DUMMY_SP, Span, Symbol};
@ -296,6 +296,7 @@ pub struct DiagInner {
pub messages: Vec<(DiagMessage, Style)>, pub messages: Vec<(DiagMessage, Style)>,
pub code: Option<ErrCode>, pub code: Option<ErrCode>,
pub lint_id: Option<LintExpectationId>,
pub span: MultiSpan, pub span: MultiSpan,
pub children: Vec<Subdiag>, pub children: Vec<Subdiag>,
pub suggestions: Suggestions, pub suggestions: Suggestions,
@ -324,6 +325,7 @@ impl DiagInner {
pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self { pub fn new_with_messages(level: Level, messages: Vec<(DiagMessage, Style)>) -> Self {
DiagInner { DiagInner {
level, level,
lint_id: None,
messages, messages,
code: None, code: None,
span: MultiSpan::new(), span: MultiSpan::new(),
@ -346,7 +348,7 @@ impl DiagInner {
match self.level { match self.level {
Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true, Level::Bug | Level::Fatal | Level::Error | Level::DelayedBug => true,
Level::ForceWarning(_) Level::ForceWarning
| Level::Warning | Level::Warning
| Level::Note | Level::Note
| Level::OnceNote | Level::OnceNote
@ -354,7 +356,7 @@ impl DiagInner {
| Level::OnceHelp | Level::OnceHelp
| Level::FailureNote | Level::FailureNote
| Level::Allow | Level::Allow
| Level::Expect(_) => false, | Level::Expect => false,
} }
} }
@ -365,7 +367,7 @@ impl DiagInner {
pub(crate) fn is_force_warn(&self) -> bool { pub(crate) fn is_force_warn(&self) -> bool {
match self.level { match self.level {
Level::ForceWarning(_) => { Level::ForceWarning => {
assert!(self.is_lint.is_some()); assert!(self.is_lint.is_some());
true true
} }
@ -1259,6 +1261,17 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
self self
} } } }
with_fn! { with_lint_id,
/// Add an argument.
#[rustc_lint_diagnostics]
pub fn lint_id(
&mut self,
id: LintExpectationId,
) -> &mut Self {
self.lint_id = Some(id);
self
} }
with_fn! { with_primary_message, with_fn! { with_primary_message,
/// Add a primary message. /// Add a primary message.
#[rustc_lint_diagnostics] #[rustc_lint_diagnostics]

View file

@ -144,7 +144,7 @@ impl Emitter for JsonEmitter {
// //
// So to avoid ICEs and confused users we "upgrade" the lint level for // So to avoid ICEs and confused users we "upgrade" the lint level for
// those `FutureBreakageItem` to warn. // those `FutureBreakageItem` to warn.
if matches!(diag.level, crate::Level::Allow | crate::Level::Expect(..)) { if matches!(diag.level, crate::Level::Allow | crate::Level::Expect) {
diag.level = crate::Level::Warning; diag.level = crate::Level::Warning;
} }
FutureBreakageItem { FutureBreakageItem {

View file

@ -905,8 +905,8 @@ impl<'a> DiagCtxtHandle<'a> {
DelayedBug => { DelayedBug => {
return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors); return self.inner.borrow_mut().emit_diagnostic(diag, self.tainted_with_errors);
} }
ForceWarning(_) | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow ForceWarning | Warning | Note | OnceNote | Help | OnceHelp | FailureNote | Allow
| Expect(_) => None, | Expect => None,
}; };
// FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic // FIXME(Centril, #69537): Consider reintroducing panic on overwriting a stashed diagnostic
@ -1045,7 +1045,7 @@ impl<'a> DiagCtxtHandle<'a> {
// Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a // Use `ForceWarning` rather than `Warning` to guarantee emission, e.g. with a
// configuration like `--cap-lints allow --force-warn bare_trait_objects`. // configuration like `--cap-lints allow --force-warn bare_trait_objects`.
inner.emit_diagnostic( inner.emit_diagnostic(
DiagInner::new(ForceWarning(None), DiagMessage::Str(warnings)), DiagInner::new(ForceWarning, DiagMessage::Str(warnings)),
None, None,
); );
} }
@ -1450,7 +1450,7 @@ impl<'a> DiagCtxtHandle<'a> {
#[rustc_lint_diagnostics] #[rustc_lint_diagnostics]
#[track_caller] #[track_caller]
pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> { pub fn struct_expect(self, msg: impl Into<DiagMessage>, id: LintExpectationId) -> Diag<'a, ()> {
Diag::new(self, Expect(id), msg) Diag::new(self, Expect, msg).with_lint_id(id)
} }
} }
@ -1510,7 +1510,7 @@ impl DiagCtxtInner {
// Future breakages aren't emitted if they're `Level::Allow` or // Future breakages aren't emitted if they're `Level::Allow` or
// `Level::Expect`, but they still need to be constructed and // `Level::Expect`, but they still need to be constructed and
// stashed below, so they'll trigger the must_produce_diag check. // stashed below, so they'll trigger the must_produce_diag check.
assert_matches!(diagnostic.level, Error | Warning | Allow | Expect(_)); assert_matches!(diagnostic.level, Error | Warning | Allow | Expect);
self.future_breakage_diagnostics.push(diagnostic.clone()); self.future_breakage_diagnostics.push(diagnostic.clone());
} }
@ -1558,7 +1558,7 @@ impl DiagCtxtInner {
}; };
} }
} }
ForceWarning(None) => {} // `ForceWarning(Some(...))` is below, with `Expect` ForceWarning if diagnostic.lint_id.is_none() => {} // `ForceWarning(Some(...))` is below, with `Expect`
Warning => { Warning => {
if !self.flags.can_emit_warnings { if !self.flags.can_emit_warnings {
// We are not emitting warnings. // We are not emitting warnings.
@ -1580,9 +1580,9 @@ impl DiagCtxtInner {
} }
return None; return None;
} }
Expect(expect_id) | ForceWarning(Some(expect_id)) => { Expect | ForceWarning => {
self.fulfilled_expectations.insert(expect_id); self.fulfilled_expectations.insert(diagnostic.lint_id.unwrap());
if let Expect(_) = diagnostic.level { if let Expect = diagnostic.level {
// Nothing emitted here for expected lints. // Nothing emitted here for expected lints.
TRACK_DIAGNOSTIC(diagnostic, &mut |_| None); TRACK_DIAGNOSTIC(diagnostic, &mut |_| None);
self.suppressed_expected_diag = true; self.suppressed_expected_diag = true;
@ -1631,7 +1631,7 @@ impl DiagCtxtInner {
if is_error { if is_error {
self.deduplicated_err_count += 1; self.deduplicated_err_count += 1;
} else if matches!(diagnostic.level, ForceWarning(_) | Warning) { } else if matches!(diagnostic.level, ForceWarning | Warning) {
self.deduplicated_warn_count += 1; self.deduplicated_warn_count += 1;
} }
self.has_printed = true; self.has_printed = true;
@ -1899,9 +1899,9 @@ pub enum Level {
/// A `force-warn` lint warning about the code being compiled. Does not prevent compilation /// A `force-warn` lint warning about the code being compiled. Does not prevent compilation
/// from finishing. /// from finishing.
/// ///
/// The [`LintExpectationId`] is used for expected lint diagnostics. In all other cases this /// Requires a [`LintExpectationId`] for expected lint diagnostics. In all other cases this
/// should be `None`. /// should be `None`.
ForceWarning(Option<LintExpectationId>), ForceWarning,
/// A warning about the code being compiled. Does not prevent compilation from finishing. /// A warning about the code being compiled. Does not prevent compilation from finishing.
/// Will be skipped if `can_emit_warnings` is false. /// Will be skipped if `can_emit_warnings` is false.
@ -1926,8 +1926,8 @@ pub enum Level {
/// Only used for lints. /// Only used for lints.
Allow, Allow,
/// Only used for lints. /// Only used for lints. Requires a [`LintExpectationId`] for silencing the lints.
Expect(LintExpectationId), Expect,
} }
impl fmt::Display for Level { impl fmt::Display for Level {
@ -1943,7 +1943,7 @@ impl Level {
Bug | Fatal | Error | DelayedBug => { Bug | Fatal | Error | DelayedBug => {
spec.set_fg(Some(Color::Red)).set_intense(true); spec.set_fg(Some(Color::Red)).set_intense(true);
} }
ForceWarning(_) | Warning => { ForceWarning | Warning => {
spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows)); spec.set_fg(Some(Color::Yellow)).set_intense(cfg!(windows));
} }
Note | OnceNote => { Note | OnceNote => {
@ -1953,7 +1953,7 @@ impl Level {
spec.set_fg(Some(Color::Cyan)).set_intense(true); spec.set_fg(Some(Color::Cyan)).set_intense(true);
} }
FailureNote => {} FailureNote => {}
Allow | Expect(_) => unreachable!(), Allow | Expect => unreachable!(),
} }
spec spec
} }
@ -1962,11 +1962,11 @@ impl Level {
match self { match self {
Bug | DelayedBug => "error: internal compiler error", Bug | DelayedBug => "error: internal compiler error",
Fatal | Error => "error", Fatal | Error => "error",
ForceWarning(_) | Warning => "warning", ForceWarning | Warning => "warning",
Note | OnceNote => "note", Note | OnceNote => "note",
Help | OnceHelp => "help", Help | OnceHelp => "help",
FailureNote => "failure-note", FailureNote => "failure-note",
Allow | Expect(_) => unreachable!(), Allow | Expect => unreachable!(),
} }
} }
@ -1977,8 +1977,7 @@ impl Level {
// Can this level be used in a subdiagnostic message? // Can this level be used in a subdiagnostic message?
fn can_be_subdiag(&self) -> bool { fn can_be_subdiag(&self) -> bool {
match self { match self {
Bug | DelayedBug | Fatal | Error | ForceWarning(_) | FailureNote | Allow Bug | DelayedBug | Fatal | Error | ForceWarning | FailureNote | Allow | Expect => false,
| Expect(_) => false,
Warning | Note | Help | OnceNote | OnceHelp => true, Warning | Note | Help | OnceNote | OnceHelp => true,
} }

View file

@ -83,6 +83,12 @@ pub struct HirId {
pub local_id: ItemLocalId, pub local_id: ItemLocalId,
} }
// To ensure correctness of incremental compilation,
// `HirId` must not implement `Ord` or `PartialOrd`.
// See https://github.com/rust-lang/rust/issues/90317.
impl !Ord for HirId {}
impl !PartialOrd for HirId {}
impl Debug for HirId { impl Debug for HirId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10) // Example: HirId(DefId(0:1 ~ aa[7697]::{use#0}).10)
@ -116,10 +122,6 @@ impl HirId {
pub fn make_owner(owner: LocalDefId) -> Self { pub fn make_owner(owner: LocalDefId) -> Self {
Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO } Self { owner: OwnerId { def_id: owner }, local_id: ItemLocalId::ZERO }
} }
pub fn index(self) -> (usize, usize) {
(rustc_index::Idx::index(self.owner.def_id), rustc_index::Idx::index(self.local_id))
}
} }
impl fmt::Display for HirId { impl fmt::Display for HirId {
@ -128,18 +130,6 @@ impl fmt::Display for HirId {
} }
} }
impl Ord for HirId {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
(self.index()).cmp(&(other.index()))
}
}
impl PartialOrd for HirId {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId); rustc_data_structures::define_stable_id_collections!(HirIdMap, HirIdSet, HirIdMapEntry, HirId);
rustc_data_structures::define_id_collections!( rustc_data_structures::define_id_collections!(
ItemLocalMap, ItemLocalMap,

View file

@ -11,6 +11,7 @@
#![feature(debug_closure_helpers)] #![feature(debug_closure_helpers)]
#![feature(exhaustive_patterns)] #![feature(exhaustive_patterns)]
#![feature(let_chains)] #![feature(let_chains)]
#![feature(negative_impls)]
#![feature(never_type)] #![feature(never_type)]
#![feature(rustc_attrs)] #![feature(rustc_attrs)]
#![feature(variant_count)] #![feature(variant_count)]

View file

@ -85,7 +85,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// Intermediate format to store the hir_id pointing to the use that resulted in the /// Intermediate format to store the hir_id pointing to the use that resulted in the
/// corresponding place being captured and a String which contains the captured value's /// corresponding place being captured and a String which contains the captured value's
/// name (i.e: a.b.c) /// name (i.e: a.b.c)
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
enum UpvarMigrationInfo { enum UpvarMigrationInfo {
/// We previously captured all of `x`, but now we capture some sub-path. /// We previously captured all of `x`, but now we capture some sub-path.
CapturingPrecise { source_expr: Option<HirId>, var_name: String }, CapturingPrecise { source_expr: Option<HirId>, var_name: String },
@ -1396,14 +1396,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
FxIndexSet::default() FxIndexSet::default()
}; };
// Combine all the captures responsible for needing migrations into one HashSet // Combine all the captures responsible for needing migrations into one IndexSet
let mut capture_diagnostic = drop_reorder_diagnostic.clone(); let mut capture_diagnostic = drop_reorder_diagnostic.clone();
for key in auto_trait_diagnostic.keys() { for key in auto_trait_diagnostic.keys() {
capture_diagnostic.insert(key.clone()); capture_diagnostic.insert(key.clone());
} }
let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>(); let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>();
capture_diagnostic.sort(); capture_diagnostic.sort_by_cached_key(|info| match info {
UpvarMigrationInfo::CapturingPrecise { source_expr: _, var_name } => {
(0, Some(var_name.clone()))
}
UpvarMigrationInfo::CapturingNothing { use_span: _ } => (1, None),
});
for captures_info in capture_diagnostic { for captures_info in capture_diagnostic {
// Get the auto trait reasons of why migration is needed because of that capture, if there are any // Get the auto trait reasons of why migration is needed because of that capture, if there are any
let capture_trait_reasons = let capture_trait_reasons =
@ -2323,8 +2328,9 @@ fn should_do_rust_2021_incompatible_closure_captures_analysis(
return false; return false;
} }
let (level, _) = let level = tcx
tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); .lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id)
.level;
!matches!(level, lint::Level::Allow) !matches!(level, lint::Level::Allow)
} }

View file

@ -29,6 +29,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LocalDefId};
use rustc_hir::intravisit::FnKind as HirFnKind; use rustc_hir::intravisit::FnKind as HirFnKind;
use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin}; use rustc_hir::{Body, FnDecl, GenericParamKind, PatKind, PredicateOrigin};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::ty::layout::LayoutOf; use rustc_middle::ty::layout::LayoutOf;
use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef}; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, Upcast, VariantDef};
@ -694,7 +695,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations {
} }
// Avoid listing trait impls if the trait is allowed. // Avoid listing trait impls if the trait is allowed.
let (level, _) = cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()); let LevelAndSource { level, .. } =
cx.tcx.lint_level_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id());
if level == Level::Allow { if level == Level::Allow {
return; return;
} }

View file

@ -17,13 +17,12 @@ use rustc_hir::def_id::{CrateNum, DefId};
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData}; use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
use rustc_hir::{Pat, PatKind}; use rustc_hir::{Pat, PatKind};
use rustc_middle::bug; use rustc_middle::bug;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::middle::privacy::EffectiveVisibilities; use rustc_middle::middle::privacy::EffectiveVisibilities;
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout}; use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths}; use rustc_middle::ty::print::{PrintError, PrintTraitRefExt as _, Printer, with_no_trimmed_paths};
use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode}; use rustc_middle::ty::{self, GenericArg, RegisteredTools, Ty, TyCtxt, TypingEnv, TypingMode};
use rustc_session::lint::{ use rustc_session::lint::{FutureIncompatibleInfo, Lint, LintBuffer, LintExpectationId, LintId};
FutureIncompatibleInfo, Level, Lint, LintBuffer, LintExpectationId, LintId,
};
use rustc_session::{LintStoreMarker, Session}; use rustc_session::{LintStoreMarker, Session};
use rustc_span::edit_distance::find_best_match_for_names; use rustc_span::edit_distance::find_best_match_for_names;
use rustc_span::{Ident, Span, Symbol, sym}; use rustc_span::{Ident, Span, Symbol, sym};
@ -573,7 +572,7 @@ pub trait LintContext {
} }
/// This returns the lint level for the given lint at the current location. /// This returns the lint level for the given lint at the current location.
fn get_lint_level(&self, lint: &'static Lint) -> Level; fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource;
/// This function can be used to manually fulfill an expectation. This can /// This function can be used to manually fulfill an expectation. This can
/// be used for lints which contain several spans, and should be suppressed, /// be used for lints which contain several spans, and should be suppressed,
@ -642,8 +641,8 @@ impl<'tcx> LintContext for LateContext<'tcx> {
} }
} }
fn get_lint_level(&self, lint: &'static Lint) -> Level { fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs).0 self.tcx.lint_level_at_node(lint, self.last_node_with_lint_attrs)
} }
} }
@ -663,8 +662,8 @@ impl LintContext for EarlyContext<'_> {
self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate) self.builder.opt_span_lint(lint, span.map(|s| s.into()), decorate)
} }
fn get_lint_level(&self, lint: &'static Lint) -> Level { fn get_lint_level(&self, lint: &'static Lint) -> LevelAndSource {
self.builder.lint_level(lint).0 self.builder.lint_level(lint)
} }
} }

View file

@ -84,10 +84,10 @@ impl LintLevelSets {
) -> LevelAndSource { ) -> LevelAndSource {
let lint = LintId::of(lint); let lint = LintId::of(lint);
let (level, mut src) = self.raw_lint_id_level(lint, idx, aux); let (level, mut src) = self.raw_lint_id_level(lint, idx, aux);
let level = reveal_actual_level(level, &mut src, sess, lint, |id| { let (level, lint_id) = reveal_actual_level(level, &mut src, sess, lint, |id| {
self.raw_lint_id_level(id, idx, aux) self.raw_lint_id_level(id, idx, aux)
}); });
(level, src) LevelAndSource { level, lint_id, src }
} }
fn raw_lint_id_level( fn raw_lint_id_level(
@ -95,17 +95,17 @@ impl LintLevelSets {
id: LintId, id: LintId,
mut idx: LintStackIndex, mut idx: LintStackIndex,
aux: Option<&FxIndexMap<LintId, LevelAndSource>>, aux: Option<&FxIndexMap<LintId, LevelAndSource>>,
) -> (Option<Level>, LintLevelSource) { ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
if let Some(specs) = aux if let Some(specs) = aux
&& let Some(&(level, src)) = specs.get(&id) && let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id)
{ {
return (Some(level), src); return (Some((level, lint_id)), src);
} }
loop { loop {
let LintSet { ref specs, parent } = self.list[idx]; let LintSet { ref specs, parent } = self.list[idx];
if let Some(&(level, src)) = specs.get(&id) { if let Some(&LevelAndSource { level, lint_id, src }) = specs.get(&id) {
return (Some(level), src); return (Some((level, lint_id)), src);
} }
if idx == COMMAND_LINE { if idx == COMMAND_LINE {
return (None, LintLevelSource::Default); return (None, LintLevelSource::Default);
@ -131,8 +131,8 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
}) })
.filter_map(|lint| { .filter_map(|lint| {
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
if matches!(lint_level, (Level::Allow, ..)) if matches!(lint_level.level, Level::Allow)
|| (matches!(lint_level, (.., LintLevelSource::Default))) || (matches!(lint_level.src, LintLevelSource::Default))
&& lint.default_level(tcx.sess.edition()) == Level::Allow && lint.default_level(tcx.sess.edition()) == Level::Allow
{ {
Some(LintId::of(lint)) Some(LintId::of(lint))
@ -379,13 +379,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) { fn visit_attribute(&mut self, attribute: &'tcx hir::Attribute) {
if matches!( if matches!(
Level::from_attr(attribute), Level::from_attr(attribute),
Some( Some((Level::Warn | Level::Deny | Level::Forbid | Level::Expect | Level::ForceWarn, _))
Level::Warn
| Level::Deny
| Level::Forbid
| Level::Expect(..)
| Level::ForceWarn(..),
)
) { ) {
let store = unerased_lint_store(self.tcx.sess); let store = unerased_lint_store(self.tcx.sess);
// Lint attributes are always a metalist inside a // Lint attributes are always a metalist inside a
@ -541,9 +535,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
for &(ref lint_name, level) in &self.sess.opts.lint_opts { for &(ref lint_name, level) in &self.sess.opts.lint_opts {
// Checks the validity of lint names derived from the command line. // Checks the validity of lint names derived from the command line.
let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name); let (tool_name, lint_name_only) = parse_lint_and_tool_name(lint_name);
if lint_name_only == crate::WARNINGS.name_lower() if lint_name_only == crate::WARNINGS.name_lower() && matches!(level, Level::ForceWarn) {
&& matches!(level, Level::ForceWarn(_))
{
self.sess self.sess
.dcx() .dcx()
.emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() }); .emit_err(UnsupportedGroup { lint_group: crate::WARNINGS.name_lower() });
@ -586,7 +578,6 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
_ => {} _ => {}
}; };
let orig_level = level;
let lint_flag_val = Symbol::intern(lint_name); let lint_flag_val = Symbol::intern(lint_name);
let Ok(ids) = self.store.find_lints(lint_name) else { let Ok(ids) = self.store.find_lints(lint_name) else {
@ -595,15 +586,15 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
}; };
for id in ids { for id in ids {
// ForceWarn and Forbid cannot be overridden // ForceWarn and Forbid cannot be overridden
if let Some((Level::ForceWarn(_) | Level::Forbid, _)) = if let Some(LevelAndSource { level: Level::ForceWarn | Level::Forbid, .. }) =
self.current_specs().get(&id) self.current_specs().get(&id)
{ {
continue; continue;
} }
if self.check_gated_lint(id, DUMMY_SP, true) { if self.check_gated_lint(id, DUMMY_SP, true) {
let src = LintLevelSource::CommandLine(lint_flag_val, orig_level); let src = LintLevelSource::CommandLine(lint_flag_val, level);
self.insert(id, (level, src)); self.insert(id, LevelAndSource { level, lint_id: None, src });
} }
} }
} }
@ -612,8 +603,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
/// Attempts to insert the `id` to `level_src` map entry. If unsuccessful /// Attempts to insert the `id` to `level_src` map entry. If unsuccessful
/// (e.g. if a forbid was already inserted on the same scope), then emits a /// (e.g. if a forbid was already inserted on the same scope), then emits a
/// diagnostic with no change to `specs`. /// diagnostic with no change to `specs`.
fn insert_spec(&mut self, id: LintId, (level, src): LevelAndSource) { fn insert_spec(&mut self, id: LintId, LevelAndSource { level, lint_id, src }: LevelAndSource) {
let (old_level, old_src) = self.provider.get_lint_level(id.lint, self.sess); let LevelAndSource { level: old_level, src: old_src, .. } =
self.provider.get_lint_level(id.lint, self.sess);
// Setting to a non-forbid level is an error if the lint previously had // Setting to a non-forbid level is an error if the lint previously had
// a forbid level. Note that this is not necessarily true even with a // a forbid level. Note that this is not necessarily true even with a
@ -685,7 +677,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
// The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself. // The lint `unfulfilled_lint_expectations` can't be expected, as it would suppress itself.
// Handling expectations of this lint would add additional complexity with little to no // Handling expectations of this lint would add additional complexity with little to no
// benefit. The expect level for this lint will therefore be ignored. // benefit. The expect level for this lint will therefore be ignored.
if let Level::Expect(_) = level if let Level::Expect = level
&& id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS) && id == LintId::of(UNFULFILLED_LINT_EXPECTATIONS)
{ {
return; return;
@ -693,13 +685,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
match (old_level, level) { match (old_level, level) {
// If the new level is an expectation store it in `ForceWarn` // If the new level is an expectation store it in `ForceWarn`
(Level::ForceWarn(_), Level::Expect(expectation_id)) => { (Level::ForceWarn, Level::Expect) => {
self.insert(id, (Level::ForceWarn(Some(expectation_id)), old_src)) self.insert(id, LevelAndSource { level: Level::ForceWarn, lint_id, src: old_src })
} }
// Keep `ForceWarn` level but drop the expectation // Keep `ForceWarn` level but drop the expectation
(Level::ForceWarn(_), _) => self.insert(id, (Level::ForceWarn(None), old_src)), (Level::ForceWarn, _) => self.insert(
id,
LevelAndSource { level: Level::ForceWarn, lint_id: None, src: old_src },
),
// Set the lint level as normal // Set the lint level as normal
_ => self.insert(id, (level, src)), _ => self.insert(id, LevelAndSource { level, lint_id, src }),
}; };
} }
@ -714,7 +709,11 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
if attr.has_name(sym::automatically_derived) { if attr.has_name(sym::automatically_derived) {
self.insert( self.insert(
LintId::of(SINGLE_USE_LIFETIMES), LintId::of(SINGLE_USE_LIFETIMES),
(Level::Allow, LintLevelSource::Default), LevelAndSource {
level: Level::Allow,
lint_id: None,
src: LintLevelSource::Default,
},
); );
continue; continue;
} }
@ -725,15 +724,22 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
.meta_item_list() .meta_item_list()
.is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden)) .is_some_and(|l| ast::attr::list_contains_name(&l, sym::hidden))
{ {
self.insert(LintId::of(MISSING_DOCS), (Level::Allow, LintLevelSource::Default)); self.insert(
LintId::of(MISSING_DOCS),
LevelAndSource {
level: Level::Allow,
lint_id: None,
src: LintLevelSource::Default,
},
);
continue; continue;
} }
let level = match Level::from_attr(attr) { let (level, lint_id) = match Level::from_attr(attr) {
None => continue, None => continue,
// This is the only lint level with a `LintExpectationId` that can be created from // This is the only lint level with a `LintExpectationId` that can be created from
// an attribute. // an attribute.
Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { Some((Level::Expect, Some(unstable_id))) if let Some(hir_id) = source_hir_id => {
let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id let LintExpectationId::Unstable { lint_index: None, attr_id: _ } = unstable_id
else { else {
bug!("stable id Level::from_attr") bug!("stable id Level::from_attr")
@ -745,9 +751,9 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
lint_index: None, lint_index: None,
}; };
Level::Expect(stable_id) (Level::Expect, Some(stable_id))
} }
Some(lvl) => lvl, Some((lvl, id)) => (lvl, id),
}; };
let Some(mut metas) = attr.meta_item_list() else { continue }; let Some(mut metas) = attr.meta_item_list() else { continue };
@ -795,13 +801,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
} }
for (lint_index, li) in metas.iter_mut().enumerate() { for (lint_index, li) in metas.iter_mut().enumerate() {
let level = match level { let mut lint_id = lint_id;
Level::Expect(mut id) => { if let Some(id) = &mut lint_id {
id.set_lint_index(Some(lint_index as u16)); id.set_lint_index(Some(lint_index as u16));
Level::Expect(id)
} }
level => level,
};
let sp = li.span(); let sp = li.span();
let meta_item = match li { let meta_item = match li {
@ -933,7 +936,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
let src = LintLevelSource::Node { name, span: sp, reason }; let src = LintLevelSource::Node { name, span: sp, reason };
for &id in ids { for &id in ids {
if self.check_gated_lint(id, sp, false) { if self.check_gated_lint(id, sp, false) {
self.insert_spec(id, (level, src)); self.insert_spec(id, LevelAndSource { level, lint_id, src });
} }
} }
@ -942,7 +945,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
// overriding the lint level but instead add an expectation that can't be // overriding the lint level but instead add an expectation that can't be
// fulfilled. The lint message will include an explanation, that the // fulfilled. The lint message will include an explanation, that the
// `unfulfilled_lint_expectations` lint can't be expected. // `unfulfilled_lint_expectations` lint can't be expected.
if let Level::Expect(expect_id) = level { if let (Level::Expect, Some(expect_id)) = (level, lint_id) {
// The `unfulfilled_lint_expectations` lint is not part of any lint // The `unfulfilled_lint_expectations` lint is not part of any lint
// groups. Therefore. we only need to check the slice if it contains a // groups. Therefore. we only need to check the slice if it contains a
// single lint. // single lint.
@ -964,7 +967,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
} }
if self.lint_added_lints && !is_crate_node { if self.lint_added_lints && !is_crate_node {
for (id, &(level, ref src)) in self.current_specs().iter() { for (id, &LevelAndSource { level, ref src, .. }) in self.current_specs().iter() {
if !id.lint.crate_level_only { if !id.lint.crate_level_only {
continue; continue;
} }
@ -1002,10 +1005,10 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
if self.lint_added_lints { if self.lint_added_lints {
let lint = builtin::UNKNOWN_LINTS; let lint = builtin::UNKNOWN_LINTS;
let (level, src) = self.lint_level(builtin::UNKNOWN_LINTS); let level = self.lint_level(builtin::UNKNOWN_LINTS);
// FIXME: make this translatable // FIXME: make this translatable
#[allow(rustc::diagnostic_outside_of_impl)] #[allow(rustc::diagnostic_outside_of_impl)]
lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { lint_level(self.sess, lint, level, Some(span.into()), |lint| {
lint.primary_message(fluent::lint_unknown_gated_lint); lint.primary_message(fluent::lint_unknown_gated_lint);
lint.arg("name", lint_id.lint.name_lower()); lint.arg("name", lint_id.lint.name_lower());
lint.note(fluent::lint_note); lint.note(fluent::lint_note);
@ -1040,8 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
span: Option<MultiSpan>, span: Option<MultiSpan>,
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
) { ) {
let (level, src) = self.lint_level(lint); let level = self.lint_level(lint);
lint_level(self.sess, lint, level, src, span, decorate) lint_level(self.sess, lint, level, span, decorate)
} }
#[track_caller] #[track_caller]
@ -1051,16 +1054,16 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
span: MultiSpan, span: MultiSpan,
decorate: impl for<'a> LintDiagnostic<'a, ()>, decorate: impl for<'a> LintDiagnostic<'a, ()>,
) { ) {
let (level, src) = self.lint_level(lint); let level = self.lint_level(lint);
lint_level(self.sess, lint, level, src, Some(span), |lint| { lint_level(self.sess, lint, level, Some(span), |lint| {
decorate.decorate_lint(lint); decorate.decorate_lint(lint);
}); });
} }
#[track_caller] #[track_caller]
pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) { pub fn emit_lint(&self, lint: &'static Lint, decorate: impl for<'a> LintDiagnostic<'a, ()>) {
let (level, src) = self.lint_level(lint); let level = self.lint_level(lint);
lint_level(self.sess, lint, level, src, None, |lint| { lint_level(self.sess, lint, level, None, |lint| {
decorate.decorate_lint(lint); decorate.decorate_lint(lint);
}); });
} }

View file

@ -159,12 +159,13 @@ impl EarlyLintPass for NonAsciiIdents {
use rustc_span::Span; use rustc_span::Span;
use unicode_security::GeneralSecurityProfile; use unicode_security::GeneralSecurityProfile;
let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).0 != Level::Allow; let check_non_ascii_idents = cx.builder.lint_level(NON_ASCII_IDENTS).level != Level::Allow;
let check_uncommon_codepoints = let check_uncommon_codepoints =
cx.builder.lint_level(UNCOMMON_CODEPOINTS).0 != Level::Allow; cx.builder.lint_level(UNCOMMON_CODEPOINTS).level != Level::Allow;
let check_confusable_idents = cx.builder.lint_level(CONFUSABLE_IDENTS).0 != Level::Allow; let check_confusable_idents =
cx.builder.lint_level(CONFUSABLE_IDENTS).level != Level::Allow;
let check_mixed_script_confusables = let check_mixed_script_confusables =
cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).0 != Level::Allow; cx.builder.lint_level(MIXED_SCRIPT_CONFUSABLES).level != Level::Allow;
if !check_non_ascii_idents if !check_non_ascii_idents
&& !check_uncommon_codepoints && !check_uncommon_codepoints

View file

@ -8,7 +8,8 @@ use rustc_data_structures::stable_hasher::{
}; };
use rustc_error_messages::{DiagMessage, MultiSpan}; use rustc_error_messages::{DiagMessage, MultiSpan};
use rustc_hir::def::Namespace; use rustc_hir::def::Namespace;
use rustc_hir::{HashStableContext, HirId, MissingLifetimeKind}; use rustc_hir::def_id::DefPathHash;
use rustc_hir::{HashStableContext, HirId, ItemLocalId, MissingLifetimeKind};
use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_macros::{Decodable, Encodable, HashStable_Generic};
pub use rustc_span::edition::Edition; pub use rustc_span::edition::Edition;
use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym}; use rustc_span::{Ident, MacroRulesNormalizedIdent, Span, Symbol, sym};
@ -102,7 +103,7 @@ pub enum Applicability {
/// The index values have a type of `u16` to reduce the size of the `LintExpectationId`. /// The index values have a type of `u16` to reduce the size of the `LintExpectationId`.
/// It's reasonable to assume that no user will define 2^16 attributes on one node or /// It's reasonable to assume that no user will define 2^16 attributes on one node or
/// have that amount of lints listed. `u16` values should therefore suffice. /// have that amount of lints listed. `u16` values should therefore suffice.
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)] #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)]
pub enum LintExpectationId { pub enum LintExpectationId {
/// Used for lints emitted during the `EarlyLintPass`. This id is not /// Used for lints emitted during the `EarlyLintPass`. This id is not
/// hash stable and should not be cached. /// hash stable and should not be cached.
@ -156,13 +157,14 @@ impl<HCX: rustc_hir::HashStableContext> HashStable<HCX> for LintExpectationId {
} }
impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId { impl<HCX: rustc_hir::HashStableContext> ToStableHashKey<HCX> for LintExpectationId {
type KeyType = (HirId, u16, u16); type KeyType = (DefPathHash, ItemLocalId, u16, u16);
#[inline] #[inline]
fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { fn to_stable_hash_key(&self, hcx: &HCX) -> Self::KeyType {
match self { match self {
LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => {
(*hir_id, *attr_index, *lint_index) let (def_path_hash, lint_idx) = hir_id.to_stable_hash_key(hcx);
(def_path_hash, lint_idx, *attr_index, *lint_index)
} }
_ => { _ => {
unreachable!("HashStable should only be called for a filled `LintExpectationId`") unreachable!("HashStable should only be called for a filled `LintExpectationId`")
@ -199,9 +201,9 @@ pub enum Level {
/// ///
/// See RFC 2383. /// See RFC 2383.
/// ///
/// The [`LintExpectationId`] is used to later link a lint emission to the actual /// Requires a [`LintExpectationId`] to later link a lint emission to the actual
/// expectation. It can be ignored in most cases. /// expectation. It can be ignored in most cases.
Expect(LintExpectationId), Expect,
/// The `warn` level will produce a warning if the lint was violated, however the /// The `warn` level will produce a warning if the lint was violated, however the
/// compiler will continue with its execution. /// compiler will continue with its execution.
Warn, Warn,
@ -209,9 +211,9 @@ pub enum Level {
/// to ensure that a lint can't be suppressed. This lint level can currently only be set /// to ensure that a lint can't be suppressed. This lint level can currently only be set
/// via the console and is therefore session specific. /// via the console and is therefore session specific.
/// ///
/// The [`LintExpectationId`] is intended to fulfill expectations marked via the /// Requires a [`LintExpectationId`] to fulfill expectations marked via the
/// `#[expect]` attribute, that will still be suppressed due to the level. /// `#[expect]` attribute, that will still be suppressed due to the level.
ForceWarn(Option<LintExpectationId>), ForceWarn,
/// The `deny` level will produce an error and stop further execution after the lint /// The `deny` level will produce an error and stop further execution after the lint
/// pass is complete. /// pass is complete.
Deny, Deny,
@ -225,9 +227,9 @@ impl Level {
pub fn as_str(self) -> &'static str { pub fn as_str(self) -> &'static str {
match self { match self {
Level::Allow => "allow", Level::Allow => "allow",
Level::Expect(_) => "expect", Level::Expect => "expect",
Level::Warn => "warn", Level::Warn => "warn",
Level::ForceWarn(_) => "force-warn", Level::ForceWarn => "force-warn",
Level::Deny => "deny", Level::Deny => "deny",
Level::Forbid => "forbid", Level::Forbid => "forbid",
} }
@ -246,24 +248,30 @@ impl Level {
} }
/// Converts an `Attribute` to a level. /// Converts an `Attribute` to a level.
pub fn from_attr(attr: &impl AttributeExt) -> Option<Self> { pub fn from_attr(attr: &impl AttributeExt) -> Option<(Self, Option<LintExpectationId>)> {
Self::from_symbol(attr.name_or_empty(), || Some(attr.id())) Self::from_symbol(attr.name_or_empty(), || Some(attr.id()))
} }
/// Converts a `Symbol` to a level. /// Converts a `Symbol` to a level.
pub fn from_symbol(s: Symbol, id: impl FnOnce() -> Option<AttrId>) -> Option<Self> { pub fn from_symbol(
s: Symbol,
id: impl FnOnce() -> Option<AttrId>,
) -> Option<(Self, Option<LintExpectationId>)> {
match s { match s {
sym::allow => Some(Level::Allow), sym::allow => Some((Level::Allow, None)),
sym::expect => { sym::expect => {
if let Some(attr_id) = id() { if let Some(attr_id) = id() {
Some(Level::Expect(LintExpectationId::Unstable { attr_id, lint_index: None })) Some((
Level::Expect,
Some(LintExpectationId::Unstable { attr_id, lint_index: None }),
))
} else { } else {
None None
} }
} }
sym::warn => Some(Level::Warn), sym::warn => Some((Level::Warn, None)),
sym::deny => Some(Level::Deny), sym::deny => Some((Level::Deny, None)),
sym::forbid => Some(Level::Forbid), sym::forbid => Some((Level::Forbid, None)),
_ => None, _ => None,
} }
} }
@ -274,8 +282,8 @@ impl Level {
Level::Deny => "-D", Level::Deny => "-D",
Level::Forbid => "-F", Level::Forbid => "-F",
Level::Allow => "-A", Level::Allow => "-A",
Level::ForceWarn(_) => "--force-warn", Level::ForceWarn => "--force-warn",
Level::Expect(_) => { Level::Expect => {
unreachable!("the expect level does not have a commandline flag") unreachable!("the expect level does not have a commandline flag")
} }
} }
@ -283,17 +291,10 @@ impl Level {
pub fn is_error(self) -> bool { pub fn is_error(self) -> bool {
match self { match self {
Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn(_) => false, Level::Allow | Level::Expect | Level::Warn | Level::ForceWarn => false,
Level::Deny | Level::Forbid => true, Level::Deny | Level::Forbid => true,
} }
} }
pub fn get_expectation_id(&self) -> Option<LintExpectationId> {
match self {
Level::Expect(id) | Level::ForceWarn(Some(id)) => Some(*id),
_ => None,
}
}
} }
/// Specification of a single lint. /// Specification of a single lint.

View file

@ -340,7 +340,7 @@ impl CStore {
} }
let level = tcx let level = tcx
.lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID) .lint_level_at_node(lint::builtin::UNUSED_CRATE_DEPENDENCIES, rustc_hir::CRATE_HIR_ID)
.0; .level;
if level != lint::Level::Allow { if level != lint::Level::Allow {
let unused_externs = let unused_externs =
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>(); self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();

View file

@ -51,8 +51,13 @@ impl LintLevelSource {
} }
} }
/// A tuple of a lint level and its source. /// Convenience helper for moving things around together that frequently are paired
pub type LevelAndSource = (Level, LintLevelSource); #[derive(Copy, Clone, Debug, HashStable, Encodable, Decodable)]
pub struct LevelAndSource {
pub level: Level,
pub lint_id: Option<LintExpectationId>,
pub src: LintLevelSource,
}
/// Return type for the `shallow_lint_levels_on` query. /// Return type for the `shallow_lint_levels_on` query.
/// ///
@ -69,14 +74,18 @@ pub struct ShallowLintLevelMap {
/// ///
/// The return of this function is suitable for diagnostics. /// The return of this function is suitable for diagnostics.
pub fn reveal_actual_level( pub fn reveal_actual_level(
level: Option<Level>, level: Option<(Level, Option<LintExpectationId>)>,
src: &mut LintLevelSource, src: &mut LintLevelSource,
sess: &Session, sess: &Session,
lint: LintId, lint: LintId,
probe_for_lint_level: impl FnOnce(LintId) -> (Option<Level>, LintLevelSource), probe_for_lint_level: impl FnOnce(
) -> Level { LintId,
)
-> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource),
) -> (Level, Option<LintExpectationId>) {
// If `level` is none then we actually assume the default level for this lint. // If `level` is none then we actually assume the default level for this lint.
let mut level = level.unwrap_or_else(|| lint.lint.default_level(sess.edition())); let (mut level, mut lint_id) =
level.unwrap_or_else(|| (lint.lint.default_level(sess.edition()), None));
// If we're about to issue a warning, check at the last minute for any // If we're about to issue a warning, check at the last minute for any
// directives against the warnings "lint". If, for example, there's an // directives against the warnings "lint". If, for example, there's an
@ -88,16 +97,17 @@ pub fn reveal_actual_level(
// future compatibility warning. // future compatibility warning.
if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) { if level == Level::Warn && lint != LintId::of(FORBIDDEN_LINT_GROUPS) {
let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS)); let (warnings_level, warnings_src) = probe_for_lint_level(LintId::of(builtin::WARNINGS));
if let Some(configured_warning_level) = warnings_level { if let Some((configured_warning_level, configured_lint_id)) = warnings_level {
if configured_warning_level != Level::Warn { if configured_warning_level != Level::Warn {
level = configured_warning_level; level = configured_warning_level;
lint_id = configured_lint_id;
*src = warnings_src; *src = warnings_src;
} }
} }
} }
// Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn // Ensure that we never exceed the `--cap-lints` argument unless the source is a --force-warn
level = if let LintLevelSource::CommandLine(_, Level::ForceWarn(_)) = src { level = if let LintLevelSource::CommandLine(_, Level::ForceWarn) = src {
level level
} else { } else {
cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid)) cmp::min(level, sess.opts.lint_cap.unwrap_or(Level::Forbid))
@ -108,7 +118,7 @@ pub fn reveal_actual_level(
level = cmp::min(*driver_level, level); level = cmp::min(*driver_level, level);
} }
level (level, lint_id)
} }
impl ShallowLintLevelMap { impl ShallowLintLevelMap {
@ -121,11 +131,11 @@ impl ShallowLintLevelMap {
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
id: LintId, id: LintId,
start: HirId, start: HirId,
) -> (Option<Level>, LintLevelSource) { ) -> (Option<(Level, Option<LintExpectationId>)>, LintLevelSource) {
if let Some(map) = self.specs.get(&start.local_id) if let Some(map) = self.specs.get(&start.local_id)
&& let Some(&(level, src)) = map.get(&id) && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
{ {
return (Some(level), src); return (Some((level, lint_id)), src);
} }
let mut owner = start.owner; let mut owner = start.owner;
@ -137,9 +147,9 @@ impl ShallowLintLevelMap {
specs = &tcx.shallow_lint_levels_on(owner).specs; specs = &tcx.shallow_lint_levels_on(owner).specs;
} }
if let Some(map) = specs.get(&parent.local_id) if let Some(map) = specs.get(&parent.local_id)
&& let Some(&(level, src)) = map.get(&id) && let Some(&LevelAndSource { level, lint_id, src }) = map.get(&id)
{ {
return (Some(level), src); return (Some((level, lint_id)), src);
} }
} }
@ -153,18 +163,18 @@ impl ShallowLintLevelMap {
tcx: TyCtxt<'_>, tcx: TyCtxt<'_>,
lint: LintId, lint: LintId,
cur: HirId, cur: HirId,
) -> (Level, LintLevelSource) { ) -> LevelAndSource {
let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur); let (level, mut src) = self.probe_for_lint_level(tcx, lint, cur);
let level = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| { let (level, lint_id) = reveal_actual_level(level, &mut src, tcx.sess, lint, |lint| {
self.probe_for_lint_level(tcx, lint, cur) self.probe_for_lint_level(tcx, lint, cur)
}); });
(level, src) LevelAndSource { level, lint_id, src }
} }
} }
impl TyCtxt<'_> { impl TyCtxt<'_> {
/// Fetch and return the user-visible lint level for the given lint at the given HirId. /// Fetch and return the user-visible lint level for the given lint at the given HirId.
pub fn lint_level_at_node(self, lint: &'static Lint, id: HirId) -> (Level, LintLevelSource) { 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) self.shallow_lint_levels_on(id.owner).lint_level_id_at_node(self, LintId::of(lint), id)
} }
} }
@ -267,8 +277,7 @@ fn explain_lint_level_source(
pub fn lint_level( pub fn lint_level(
sess: &Session, sess: &Session,
lint: &'static Lint, lint: &'static Lint,
level: Level, level: LevelAndSource,
src: LintLevelSource,
span: Option<MultiSpan>, span: Option<MultiSpan>,
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
) { ) {
@ -278,11 +287,12 @@ pub fn lint_level(
fn lint_level_impl( fn lint_level_impl(
sess: &Session, sess: &Session,
lint: &'static Lint, lint: &'static Lint,
level: Level, level: LevelAndSource,
src: LintLevelSource,
span: Option<MultiSpan>, span: Option<MultiSpan>,
decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>, decorate: Box<dyn '_ + for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>)>,
) { ) {
let LevelAndSource { level, lint_id, src } = level;
// Check for future incompatibility lints and issue a stronger warning. // Check for future incompatibility lints and issue a stronger warning.
let future_incompatible = lint.future_incompatible; let future_incompatible = lint.future_incompatible;
@ -301,7 +311,7 @@ pub fn lint_level(
return; return;
} }
} }
Level::Expect(expect_id) => { Level::Expect => {
// This case is special as we actually allow the lint itself in this context, but // This case is special as we actually allow the lint itself in this context, but
// we can't return early like in the case for `Level::Allow` because we still // we can't return early like in the case for `Level::Allow` because we still
// need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`. // need the lint diagnostic to be emitted to `rustc_error::DiagCtxtInner`.
@ -309,10 +319,9 @@ pub fn lint_level(
// We can also not mark the lint expectation as fulfilled here right away, as it // We can also not mark the lint expectation as fulfilled here right away, as it
// can still be cancelled in the decorate function. All of this means that we simply // can still be cancelled in the decorate function. All of this means that we simply
// create a `Diag` and continue as we would for warnings. // create a `Diag` and continue as we would for warnings.
rustc_errors::Level::Expect(expect_id) rustc_errors::Level::Expect
} }
Level::ForceWarn(Some(expect_id)) => rustc_errors::Level::ForceWarning(Some(expect_id)), Level::ForceWarn => rustc_errors::Level::ForceWarning,
Level::ForceWarn(None) => rustc_errors::Level::ForceWarning(None),
Level::Warn => rustc_errors::Level::Warning, Level::Warn => rustc_errors::Level::Warning,
Level::Deny | Level::Forbid => rustc_errors::Level::Error, Level::Deny | Level::Forbid => rustc_errors::Level::Error,
}; };
@ -320,6 +329,9 @@ pub fn lint_level(
if let Some(span) = span { if let Some(span) = span {
err.span(span); err.span(span);
} }
if let Some(lint_id) = lint_id {
err.lint_id(lint_id);
}
// If this code originates in a foreign macro, aka something that this crate // If this code originates in a foreign macro, aka something that this crate
// did not itself author, then it's likely that there's nothing this crate // did not itself author, then it's likely that there's nothing this crate
@ -350,7 +362,7 @@ pub fn lint_level(
// the compiler. It is therefore not necessary to add any information for the user. // the compiler. It is therefore not necessary to add any information for the user.
// This will therefore directly call the decorate function which will in turn emit // This will therefore directly call the decorate function which will in turn emit
// the diagnostic. // the diagnostic.
if let Level::Expect(_) = level { if let Level::Expect = level {
decorate(&mut err); decorate(&mut err);
err.emit(); err.emit();
return; return;
@ -419,5 +431,5 @@ pub fn lint_level(
explain_lint_level_source(lint, level, src, &mut err); explain_lint_level_source(lint, level, src, &mut err);
err.emit() err.emit()
} }
lint_level_impl(sess, lint, level, src, span, Box::new(decorate)) lint_level_impl(sess, lint, level, span, Box::new(decorate))
} }

View file

@ -255,7 +255,7 @@ fn late_report_deprecation(
// Calculating message for lint involves calling `self.def_path_str`, // Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query. // which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway. // Skip all that work if the lint is allowed anyway.
if tcx.lint_level_at_node(lint, hir_id).0 == Level::Allow { if tcx.lint_level_at_node(lint, hir_id).level == Level::Allow {
return; return;
} }

View file

@ -3022,8 +3022,8 @@ impl<'tcx> TyCtxt<'tcx> {
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
decorator: impl for<'a> LintDiagnostic<'a, ()>, decorator: impl for<'a> LintDiagnostic<'a, ()>,
) { ) {
let (level, src) = self.lint_level_at_node(lint, hir_id); let level = self.lint_level_at_node(lint, hir_id);
lint_level(self.sess, lint, level, src, Some(span.into()), |lint| { lint_level(self.sess, lint, level, Some(span.into()), |lint| {
decorator.decorate_lint(lint); decorator.decorate_lint(lint);
}) })
} }
@ -3040,8 +3040,8 @@ impl<'tcx> TyCtxt<'tcx> {
span: impl Into<MultiSpan>, span: impl Into<MultiSpan>,
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
) { ) {
let (level, src) = self.lint_level_at_node(lint, hir_id); let level = self.lint_level_at_node(lint, hir_id);
lint_level(self.sess, lint, level, src, Some(span.into()), decorate); lint_level(self.sess, lint, level, Some(span.into()), decorate);
} }
/// Find the crate root and the appropriate span where `use` and outer attributes can be /// Find the crate root and the appropriate span where `use` and outer attributes can be
@ -3108,8 +3108,8 @@ impl<'tcx> TyCtxt<'tcx> {
id: HirId, id: HirId,
decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>), decorate: impl for<'a, 'b> FnOnce(&'b mut Diag<'a, ()>),
) { ) {
let (level, src) = self.lint_level_at_node(lint, id); let level = self.lint_level_at_node(lint, id);
lint_level(self.sess, lint, level, src, None, decorate); lint_level(self.sess, lint, level, None, decorate);
} }
pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> { pub fn in_scope_traits(self, id: HirId) -> Option<&'tcx [TraitCandidate]> {

View file

@ -159,7 +159,7 @@ fn find_capture_matching_projections<'a, 'tcx>(
) -> Option<(usize, &'a Capture<'tcx>)> { ) -> Option<(usize, &'a Capture<'tcx>)> {
let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections); let hir_projections = convert_to_hir_projections_and_truncate_for_capture(projections);
upvars.get_by_key_enumerated(var_hir_id.0).find(|(_, capture)| { upvars.get_by_key_enumerated(var_hir_id.0.local_id).find(|(_, capture)| {
let possible_ancestor_proj_kinds: Vec<_> = let possible_ancestor_proj_kinds: Vec<_> =
capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect(); capture.captured_place.place.projections.iter().map(|proj| proj.kind).collect();
is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections) is_ancestor_or_same_capture(&possible_ancestor_proj_kinds, &hir_projections)

View file

@ -13,7 +13,7 @@ use rustc_data_structures::sorted_map::SortedIndexMultiMap;
use rustc_errors::ErrorGuaranteed; use rustc_errors::ErrorGuaranteed;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Node}; use rustc_hir::{self as hir, BindingMode, ByRef, HirId, ItemLocalId, Node};
use rustc_index::bit_set::GrowableBitSet; use rustc_index::bit_set::GrowableBitSet;
use rustc_index::{Idx, IndexSlice, IndexVec}; use rustc_index::{Idx, IndexSlice, IndexVec};
use rustc_infer::infer::{InferCtxt, TyCtxtInferExt}; use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
@ -221,7 +221,7 @@ struct Builder<'a, 'tcx> {
coverage_info: Option<coverageinfo::CoverageInfoBuilder>, coverage_info: Option<coverageinfo::CoverageInfoBuilder>,
} }
type CaptureMap<'tcx> = SortedIndexMultiMap<usize, HirId, Capture<'tcx>>; type CaptureMap<'tcx> = SortedIndexMultiMap<usize, ItemLocalId, Capture<'tcx>>;
#[derive(Debug)] #[derive(Debug)]
struct Capture<'tcx> { struct Capture<'tcx> {
@ -853,6 +853,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let capture_tys = upvar_args.upvar_tys(); let capture_tys = upvar_args.upvar_tys();
let tcx = self.tcx; let tcx = self.tcx;
let mut upvar_owner = None;
self.upvars = tcx self.upvars = tcx
.closure_captures(self.def_id) .closure_captures(self.def_id)
.iter() .iter()
@ -866,6 +867,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, HirPlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id,
_ => bug!("Expected an upvar"), _ => bug!("Expected an upvar"),
}; };
let upvar_base = upvar_owner.get_or_insert(var_id.owner);
assert_eq!(*upvar_base, var_id.owner);
let var_id = var_id.local_id;
let mutability = captured_place.mutability; let mutability = captured_place.mutability;

View file

@ -195,7 +195,7 @@ impl<'tcx> UnsafetyVisitor<'_, 'tcx> {
/// Whether the `unsafe_op_in_unsafe_fn` lint is `allow`ed at the current HIR node. /// 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 { fn unsafe_op_in_unsafe_fn_allowed(&self) -> bool {
self.tcx.lint_level_at_node(UNSAFE_OP_IN_UNSAFE_FN, self.hir_context).0 == Level::Allow self.tcx.lint_level_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. /// Handle closures/coroutines/inline-consts, which is unsafecked with their parent body.
@ -292,8 +292,10 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
}); });
} }
BlockSafety::ExplicitUnsafe(hir_id) => { BlockSafety::ExplicitUnsafe(hir_id) => {
let used = let used = matches!(
matches!(self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id), (Level::Allow, _)); self.tcx.lint_level_at_node(UNUSED_UNSAFE, hir_id).level,
Level::Allow
);
self.in_safety_context( self.in_safety_context(
SafetyContext::UnsafeBlock { SafetyContext::UnsafeBlock {
span: block.span, span: block.span,

View file

@ -1025,7 +1025,7 @@ fn find_fallback_pattern_typo<'tcx>(
pat: &Pat<'tcx>, pat: &Pat<'tcx>,
lint: &mut UnreachablePattern<'_>, lint: &mut UnreachablePattern<'_>,
) { ) {
if let (Level::Allow, _) = cx.tcx.lint_level_at_node(UNREACHABLE_PATTERNS, hir_id) { if let Level::Allow = cx.tcx.lint_level_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 // 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. // ICE. At the same time, we don't really need to do all of this if we won't emit anything.
return; return;

View file

@ -19,8 +19,8 @@ use rustc_middle::middle::privacy::Level;
use rustc_middle::query::Providers; use rustc_middle::query::Providers;
use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::{bug, span_bug}; use rustc_middle::{bug, span_bug};
use rustc_session::lint;
use rustc_session::lint::builtin::DEAD_CODE; use rustc_session::lint::builtin::DEAD_CODE;
use rustc_session::lint::{self, LintExpectationId};
use rustc_span::{Symbol, sym}; use rustc_span::{Symbol, sym};
use crate::errors::{ use crate::errors::{
@ -696,8 +696,8 @@ fn has_allow_dead_code_or_lang_attr(
fn has_allow_expect_dead_code(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { 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 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).0; let lint_level = tcx.lint_level_at_node(lint::builtin::DEAD_CODE, hir_id).level;
matches!(lint_level, lint::Allow | lint::Expect(_)) matches!(lint_level, lint::Allow | lint::Expect)
} }
fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { fn has_used_like_attr(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
@ -915,7 +915,7 @@ fn live_symbols_and_ignored_derived_traits(
struct DeadItem { struct DeadItem {
def_id: LocalDefId, def_id: LocalDefId,
name: Symbol, name: Symbol,
level: lint::Level, level: (lint::Level, Option<LintExpectationId>),
} }
struct DeadVisitor<'tcx> { struct DeadVisitor<'tcx> {
@ -959,9 +959,10 @@ impl<'tcx> DeadVisitor<'tcx> {
ShouldWarnAboutField::Yes ShouldWarnAboutField::Yes
} }
fn def_lint_level(&self, id: LocalDefId) -> lint::Level { fn def_lint_level(&self, id: LocalDefId) -> (lint::Level, Option<LintExpectationId>) {
let hir_id = self.tcx.local_def_id_to_hir_id(id); let hir_id = self.tcx.local_def_id_to_hir_id(id);
self.tcx.lint_level_at_node(DEAD_CODE, hir_id).0 let level = self.tcx.lint_level_at_node(DEAD_CODE, hir_id);
(level.level, level.lint_id)
} }
// # Panics // # Panics
@ -1129,7 +1130,8 @@ impl<'tcx> DeadVisitor<'tcx> {
if dead_codes.is_empty() { if dead_codes.is_empty() {
return; return;
} }
dead_codes.sort_by_key(|v| v.level); // 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) { for group in dead_codes.chunk_by(|a, b| a.level == b.level) {
self.lint_at_single_level(&group, participle, Some(def_id), report_on); self.lint_at_single_level(&group, participle, Some(def_id), report_on);
} }

View file

@ -980,7 +980,7 @@ impl<'tcx> Visitor<'tcx> for Checker<'tcx> {
// Calculating message for lint involves calling `self.def_path_str`, // Calculating message for lint involves calling `self.def_path_str`,
// which will by default invoke the expensive `visible_parent_map` query. // which will by default invoke the expensive `visible_parent_map` query.
// Skip all that work if the lint is allowed anyway. // Skip all that work if the lint is allowed anyway.
if self.tcx.lint_level_at_node(DEPRECATED, id).0 if self.tcx.lint_level_at_node(DEPRECATED, id).level
== lint::Level::Allow == lint::Level::Allow
{ {
return; return;

View file

@ -1,3 +1,4 @@
use rustc_middle::lint::LevelAndSource;
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
use rustc_span::ErrorGuaranteed; use rustc_span::ErrorGuaranteed;
use tracing::instrument; use tracing::instrument;
@ -64,7 +65,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
scrut_ty: RevealedTy<'tcx>, scrut_ty: RevealedTy<'tcx>,
) -> Result<(), ErrorGuaranteed> { ) -> Result<(), ErrorGuaranteed> {
if !matches!( if !matches!(
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0, rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).level,
rustc_session::lint::Level::Allow rustc_session::lint::Level::Allow
) { ) {
let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?; let witnesses = collect_nonexhaustive_missing_variants(rcx, pat_column)?;
@ -88,13 +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 // arm. This no longer makes sense so we warn users, to avoid silently breaking their
// usage of the lint. // usage of the lint.
for arm in arms { for arm in arms {
let (lint_level, lint_level_source) = let LevelAndSource { level, src, .. } =
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data); rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
if !matches!(lint_level, rustc_session::lint::Level::Allow) { if !matches!(level, rustc_session::lint::Level::Allow) {
let decorator = NonExhaustiveOmittedPatternLintOnArm { let decorator = NonExhaustiveOmittedPatternLintOnArm {
lint_span: lint_level_source.span(), lint_span: src.span(),
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()), suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
lint_level: lint_level.as_str(), lint_level: level.as_str(),
lint_name: "non_exhaustive_omitted_patterns", lint_name: "non_exhaustive_omitted_patterns",
}; };

View file

@ -1700,7 +1700,7 @@ pub fn get_cmd_lint_options(
let mut lint_opts_with_position = vec![]; let mut lint_opts_with_position = vec![];
let mut describe_lints = false; let mut describe_lints = false;
for level in [lint::Allow, lint::Warn, lint::ForceWarn(None), lint::Deny, lint::Forbid] { for level in [lint::Allow, lint::Warn, lint::ForceWarn, lint::Deny, lint::Forbid] {
for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) { for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
if lint_name == "help" { if lint_name == "help" {
describe_lints = true; describe_lints = true;

View file

@ -5,7 +5,7 @@ use std::ops;
use rustc_hir as hir; use rustc_hir as hir;
use rustc_lint::builtin::MISSING_DOCS; use rustc_lint::builtin::MISSING_DOCS;
use rustc_middle::lint::LintLevelSource; use rustc_middle::lint::{LevelAndSource, LintLevelSource};
use rustc_session::lint; use rustc_session::lint;
use rustc_span::FileName; use rustc_span::FileName;
use serde::Serialize; use serde::Serialize;
@ -216,7 +216,8 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> {
let has_doc_example = tests.found_tests != 0; let has_doc_example = tests.found_tests != 0;
let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap(); let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id); let LevelAndSource { level, src, .. } =
self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
// In case we have: // In case we have:
// //
@ -251,7 +252,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> {
// unless the user had an explicit `allow`. // unless the user had an explicit `allow`.
// //
let should_have_docs = !should_be_ignored let should_have_docs = !should_be_ignored
&& (level != lint::Level::Allow || matches!(source, LintLevelSource::Default)); && (level != lint::Level::Allow || matches!(src, LintLevelSource::Default));
if let Some(span) = i.span(self.ctx.tcx) { if let Some(span) = i.span(self.ctx.tcx) {
let filename = span.filename(self.ctx.sess()); let filename = span.filename(self.ctx.sess());

View file

@ -6,7 +6,7 @@
//! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests. //! - PRIVATE_DOC_TESTS: this lint is **STABLE** and looks for private items with doctests.
use rustc_hir as hir; use rustc_hir as hir;
use rustc_middle::lint::LintLevelSource; use rustc_middle::lint::{LevelAndSource, LintLevelSource};
use rustc_session::lint; use rustc_session::lint;
use tracing::debug; use tracing::debug;
@ -107,11 +107,11 @@ pub(crate) fn should_have_doc_example(cx: &DocContext<'_>, item: &clean::Item) -
{ {
return false; return false;
} }
let (level, source) = cx.tcx.lint_level_at_node( let LevelAndSource { level, src, .. } = cx.tcx.lint_level_at_node(
crate::lint::MISSING_DOC_CODE_EXAMPLES, crate::lint::MISSING_DOC_CODE_EXAMPLES,
cx.tcx.local_def_id_to_hir_id(def_id), cx.tcx.local_def_id_to_hir_id(def_id),
); );
level != lint::Level::Allow || matches!(source, LintLevelSource::Default) level != lint::Level::Allow || matches!(src, LintLevelSource::Default)
} }
pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) { pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) {

View file

@ -199,7 +199,7 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: Msrv, expr: &Expr<'_>) {
&& !expr.span.from_expansion() && !expr.span.from_expansion()
&& !inner.span.from_expansion() && !inner.span.from_expansion()
&& let Some(suggestion) = simplify_not(cx, msrv, inner) && let Some(suggestion) = simplify_not(cx, msrv, inner)
&& cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).level != Level::Allow
{ {
use clippy_utils::sugg::{Sugg, has_enclosing_paren}; use clippy_utils::sugg::{Sugg, has_enclosing_paren};
let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) { let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) {
@ -605,7 +605,7 @@ impl<'tcx> NonminimalBoolVisitor<'_, 'tcx> {
} }
} }
let nonminimal_bool_lint = |mut suggestions: Vec<_>| { let nonminimal_bool_lint = |mut suggestions: Vec<_>| {
if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).0 != Level::Allow { if self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, e.hir_id).level != Level::Allow {
suggestions.sort(); suggestions.sort();
span_lint_hir_and_then( span_lint_hir_and_then(
self.cx, self.cx,

View file

@ -69,7 +69,7 @@ impl EarlyLintPass for DisallowedScriptIdents {
// Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint: // Implementation is heavily inspired by the implementation of [`non_ascii_idents`] lint:
// https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs // https://github.com/rust-lang/rust/blob/master/compiler/rustc_lint/src/non_ascii_idents.rs
let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).0 != Level::Allow; let check_disallowed_script_idents = cx.builder.lint_level(DISALLOWED_SCRIPT_IDENTS).level != Level::Allow;
if !check_disallowed_script_idents { if !check_disallowed_script_idents {
return; return;
} }

View file

@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind}; use rustc_ast::ast::{Crate, Inline, Item, ItemKind, ModKind};
use rustc_errors::MultiSpan; use rustc_errors::MultiSpan;
use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext}; use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
use rustc_middle::lint::LevelAndSource;
use rustc_session::impl_lint_pass; use rustc_session::impl_lint_pass;
use rustc_span::{FileName, Span}; use rustc_span::{FileName, Span};
use std::collections::BTreeMap; use std::collections::BTreeMap;
@ -45,11 +46,10 @@ declare_clippy_lint! {
"file loaded as module multiple times" "file loaded as module multiple times"
} }
#[derive(PartialOrd, Ord, PartialEq, Eq)]
struct Modules { struct Modules {
local_path: PathBuf, local_path: PathBuf,
spans: Vec<Span>, spans: Vec<Span>,
lint_levels: Vec<Level>, lint_levels: Vec<LevelAndSource>,
} }
#[derive(Default)] #[derive(Default)]
@ -95,11 +95,11 @@ impl EarlyLintPass for DuplicateMod {
.iter() .iter()
.zip(lint_levels) .zip(lint_levels)
.filter_map(|(span, lvl)| { .filter_map(|(span, lvl)| {
if let Some(id) = lvl.get_expectation_id() { if let Some(id) = lvl.lint_id {
cx.fulfill_expectation(id); cx.fulfill_expectation(id);
} }
(!matches!(lvl, Level::Allow | Level::Expect(_))).then_some(*span) (!matches!(lvl.level, Level::Allow | Level::Expect)).then_some(*span)
}) })
.collect(); .collect();

View file

@ -408,9 +408,9 @@ mod zombie_processes;
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation}; use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage; use clippy_utils::macros::FormatArgsStorage;
use utils::attr_collector::{AttrCollector, AttrStorage};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId}; use rustc_lint::{Lint, LintId};
use utils::attr_collector::{AttrCollector, AttrStorage};
/// Register all pre expansion lints /// Register all pre expansion lints
/// ///

View file

@ -153,8 +153,14 @@ impl LateLintPass<'_> for MacroUseImports {
[] | [_] => return, [] | [_] => return,
[root, item] => { [root, item] => {
if !check_dup.contains(&(*item).to_string()) { if !check_dup.contains(&(*item).to_string()) {
used.entry(((*root).to_string(), span, hir_id)) used.entry((
.or_insert_with(Vec::new) (*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push((*item).to_string()); .push((*item).to_string());
check_dup.push((*item).to_string()); check_dup.push((*item).to_string());
} }
@ -171,14 +177,26 @@ impl LateLintPass<'_> for MacroUseImports {
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
used.entry(((*root).to_string(), span, hir_id)) used.entry((
.or_insert_with(Vec::new) (*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push(filtered.join("::")); .push(filtered.join("::"));
check_dup.extend(filtered); check_dup.extend(filtered);
} else { } else {
let rest = rest.to_vec(); let rest = rest.to_vec();
used.entry(((*root).to_string(), span, hir_id)) used.entry((
.or_insert_with(Vec::new) (*root).to_string(),
span,
hir_id.local_id,
cx.tcx.def_path_hash(hir_id.owner.def_id.into()),
))
.or_insert_with(|| (vec![], hir_id))
.0
.push(rest.join("::")); .push(rest.join("::"));
check_dup.extend(rest.iter().map(ToString::to_string)); check_dup.extend(rest.iter().map(ToString::to_string));
} }
@ -190,7 +208,7 @@ impl LateLintPass<'_> for MacroUseImports {
// If mac_refs is not empty we have encountered an import we could not handle // If mac_refs is not empty we have encountered an import we could not handle
// such as `std::prelude::v1::foo` or some other macro that expands to an import. // such as `std::prelude::v1::foo` or some other macro that expands to an import.
if self.mac_refs.is_empty() { if self.mac_refs.is_empty() {
for ((root, span, hir_id), path) in used { for ((root, span, ..), (path, hir_id)) in used {
let import = if let [single] = &path[..] { let import = if let [single] = &path[..] {
format!("{root}::{single}") format!("{root}::{single}")
} else { } else {

View file

@ -73,8 +73,8 @@ impl_lint_pass!(ModStyle => [MOD_MODULE_FILES, SELF_NAMED_MODULE_FILES]);
impl EarlyLintPass for ModStyle { impl EarlyLintPass for ModStyle {
fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) { fn check_crate(&mut self, cx: &EarlyContext<'_>, _: &ast::Crate) {
if cx.builder.lint_level(MOD_MODULE_FILES).0 == Level::Allow if cx.builder.lint_level(MOD_MODULE_FILES).level == Level::Allow
&& cx.builder.lint_level(SELF_NAMED_MODULE_FILES).0 == Level::Allow && cx.builder.lint_level(SELF_NAMED_MODULE_FILES).level == Level::Allow
{ {
return; return;
} }

View file

@ -138,7 +138,7 @@ impl RawStrings {
); );
}, },
); );
if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS), rustc_lint::Allow) { if !matches!(cx.get_lint_level(NEEDLESS_RAW_STRINGS).level, rustc_lint::Allow) {
return; return;
} }
} }

View file

@ -404,7 +404,7 @@ fn check_final_expr<'tcx>(
match cx.tcx.hir_attrs(expr.hir_id) { match cx.tcx.hir_attrs(expr.hir_id) {
[] => {}, [] => {},
[attr] => { [attr] => {
if matches!(Level::from_attr(attr), Some(Level::Expect(_))) if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
&& let metas = attr.meta_item_list() && let metas = attr.meta_item_list()
&& let Some(lst) = metas && let Some(lst) = metas
&& let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice() && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()

View file

@ -114,6 +114,7 @@ use rustc_hir::{
use rustc_lexer::{TokenKind, tokenize}; use rustc_lexer::{TokenKind, tokenize};
use rustc_lint::{LateContext, Level, Lint, LintContext}; use rustc_lint::{LateContext, Level, Lint, LintContext};
use rustc_middle::hir::place::PlaceBase; use rustc_middle::hir::place::PlaceBase;
use rustc_middle::lint::LevelAndSource;
use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind};
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::fast_reject::SimplifiedType;
@ -1976,14 +1977,14 @@ pub fn fulfill_or_allowed(cx: &LateContext<'_>, lint: &'static Lint, ids: impl I
let mut suppress_lint = false; let mut suppress_lint = false;
for id in ids { for id in ids {
let (level, _) = cx.tcx.lint_level_at_node(lint, id); let LevelAndSource { level, lint_id, .. } = cx.tcx.lint_level_at_node(lint, id);
if let Some(expectation) = level.get_expectation_id() { if let Some(expectation) = lint_id {
cx.fulfill_expectation(expectation); cx.fulfill_expectation(expectation);
} }
match level { match level {
Level::Allow | Level::Expect(_) => suppress_lint = true, Level::Allow | Level::Expect => suppress_lint = true,
Level::Warn | Level::ForceWarn(_) | Level::Deny | Level::Forbid => {}, Level::Warn | Level::ForceWarn | Level::Deny | Level::Forbid => {},
} }
} }
@ -1998,7 +1999,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 /// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
/// expectations at the checked nodes will be fulfilled. /// expectations at the checked nodes will be fulfilled.
pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool { pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow cx.tcx.lint_level_at_node(lint, id).level == Level::Allow
} }
pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> { pub fn strip_pat_refs<'hir>(mut pat: &'hir Pat<'hir>) -> &'hir Pat<'hir> {