From 9fef3d9e0a386f8b1e277d4b598e6254c9c0516b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 6 Aug 2021 23:18:16 +0200 Subject: [PATCH 01/13] Added `Expect` lint level and attribute (RFC-2383) * Also added the `LintExpectationId` which will be used in future commits --- Cargo.lock | 1 + .../src/annotate_snippet_emitter_writer.rs | 1 + compiler/rustc_errors/src/diagnostic.rs | 6 +- compiler/rustc_errors/src/lib.rs | 14 ++++- compiler/rustc_feature/src/builtin_attrs.rs | 4 ++ compiler/rustc_lint/src/context.rs | 4 ++ compiler/rustc_lint_defs/Cargo.toml | 1 + compiler/rustc_lint_defs/src/lib.rs | 56 ++++++++++++++++++- compiler/rustc_middle/src/lint.rs | 22 ++++++++ compiler/rustc_session/src/session.rs | 3 + 10 files changed, 107 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f0c6e371c38be..bc2c77d9271f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3882,6 +3882,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_data_structures", + "rustc_index", "rustc_macros", "rustc_serialize", "rustc_span", diff --git a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs index 7d7ab1ed4e5e6..c380455012d0d 100644 --- a/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs +++ b/compiler/rustc_errors/src/annotate_snippet_emitter_writer.rs @@ -75,6 +75,7 @@ fn annotation_type_for_level(level: Level) -> AnnotationType { // FIXME(#59346): Not sure how to map this level Level::FailureNote => AnnotationType::Error, Level::Allow => panic!("Should not call with Allow"), + Level::Expect(_) => panic!("Should not call with Expect"), } } diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 6d6ada86428db..802f25606010f 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -133,7 +133,11 @@ impl Diagnostic { | Level::Error { .. } | Level::FailureNote => true, - Level::Warning | Level::Note | Level::Help | Level::Allow => false, + Level::Warning + | Level::Note + | Level::Help + | Level::Allow + | Level::Expect(_) => false, } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index fdfedffc52985..41d2b28599738 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -20,6 +20,7 @@ extern crate tracing; pub use emitter::ColorConfig; +use rustc_lint_defs::LintExpectationId; use Level::*; use emitter::{is_case_difference, Emitter, EmitterWriter}; @@ -677,6 +678,11 @@ impl Handler { DiagnosticBuilder::new(self, Level::Allow, msg) } + /// Construct a builder at the `Expect` level with the `msg`. + pub fn struct_expect(&self, msg: &str, id: LintExpectationId) -> DiagnosticBuilder<'_, ()> { + DiagnosticBuilder::new(self, Level::Expect(id), msg) + } + /// Construct a builder at the `Error` level at the given `span` and with the `msg`. pub fn struct_span_err( &self, @@ -953,7 +959,9 @@ impl HandlerInner { (*TRACK_DIAGNOSTICS)(diagnostic); - if diagnostic.level == Allow { + if let Level::Expect(_) = diagnostic.level { + return; + } else if diagnostic.level == Allow { return; } @@ -1250,6 +1258,7 @@ pub enum Level { Help, FailureNote, Allow, + Expect(LintExpectationId), } impl fmt::Display for Level { @@ -1275,7 +1284,7 @@ impl Level { spec.set_fg(Some(Color::Cyan)).set_intense(true); } FailureNote => {} - Allow => unreachable!(), + Allow | Expect(_) => unreachable!(), } spec } @@ -1289,6 +1298,7 @@ impl Level { Help => "help", FailureNote => "failure-note", Allow => panic!("Shouldn't call on allowed error"), + Expect(_) => panic!("Shouldn't call on expected error"), } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 53762eef78592..4b9cf784495fe 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -282,6 +282,10 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[ ungated!( allow, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk ), + gated!( + expect, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk, + lint_reasons, experimental!(expect) + ), ungated!( forbid, Normal, template!(List: r#"lint1, lint2, ..., /*opt*/ reason = "...""#), DuplicatesOk ), diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index ad9a16fb39ae2..c34e9f1ba7831 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -109,6 +109,7 @@ struct LintGroup { depr: Option, } +#[derive(Debug)] pub enum CheckLintNameResult<'a> { Ok(&'a [LintId]), /// Lint doesn't exist. Potentially contains a suggestion for a correct lint name. @@ -377,6 +378,9 @@ impl LintStore { Level::ForceWarn => "--force-warn", Level::Deny => "-D", Level::Forbid => "-F", + Level::Expect(_) => { + unreachable!("lints with the level of `expect` should not run this code"); + } }, lint_name ); diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index 798d50819e2d8..c37e45b46ce96 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -10,3 +10,4 @@ rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } rustc_target = { path = "../rustc_target" } +rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e9c62fc400651..e6643cfd544f1 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -1,3 +1,5 @@ +#![feature(min_specialization)] + #[macro_use] extern crate rustc_macros; @@ -46,13 +48,60 @@ pub enum Applicability { Unspecified, } +rustc_index::newtype_index! { + /// FIXME: The lint expectation ID is currently a simple copy of the `AttrId` + /// that the expectation originated from. In the future it should be generated + /// by other means. This is for one to keep the IDs independent of each other + /// and also to ensure that it is actually stable between compilation sessions. + /// (The `AttrId` for instance, is not stable). + /// + /// Additionally, it would be nice if this generation could be moved into + /// [`Level::from_symbol`] to have it all contained in one module and to + /// make it simpler to use. + pub struct LintExpectationId { + DEBUG_FORMAT = "LintExpectationId({})" + } +} + +rustc_data_structures::impl_stable_hash_via_hash!(LintExpectationId); + +impl ToStableHashKey for LintExpectationId { + type KeyType = u32; + + #[inline] + fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { + self.as_u32() + } +} + /// Setting for how to handle a lint. +/// +/// See: https://doc.rust-lang.org/rustc/lints/levels.html #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum Level { + /// The `allow` level will not issue any message. Allow, + /// The `expect` level will suppress the lint message but intern produce a message + /// if the lint wasn't issued in the expected scope. `Expect` should not be used as + /// an initial level for a lint. + /// + /// Note that this still means that the lint is enabled in this position and should + /// be emitted, this will intern fulfill the expectation and suppress the lint. + /// + /// See RFC 2383. + /// + /// The `LintExpectationId` is used to later link a lint emission to the actual + /// expectation. It can be ignored in most cases. + Expect(LintExpectationId), + /// The `warn` level will produce a warning if the lint was violated, however the + /// compiler will continue with its execution. Warn, ForceWarn, + /// The `deny` level will produce an error and stop further execution after the lint + /// pass is complete. Deny, + /// `Forbid` is equivalent to the `deny` level but can't be overwritten like the previous + /// levels. Forbid, } @@ -63,6 +112,7 @@ impl Level { pub fn as_str(self) -> &'static str { match self { Level::Allow => "allow", + Level::Expect(_) => "expect", Level::Warn => "warn", Level::ForceWarn => "force-warn", Level::Deny => "deny", @@ -70,14 +120,15 @@ impl Level { } } - /// Converts a lower-case string to a level. + /// Converts a lower-case string to a level. This will never construct the expect + /// level as that would require a [`LintExpectationId`] pub fn from_str(x: &str) -> Option { match x { "allow" => Some(Level::Allow), "warn" => Some(Level::Warn), "deny" => Some(Level::Deny), "forbid" => Some(Level::Forbid), - _ => None, + "expect" | _ => None, } } @@ -85,6 +136,7 @@ impl Level { pub fn from_symbol(x: Symbol) -> Option { match x { sym::allow => Some(Level::Allow), + sym::expect => Some(Level::Expect(LintExpectationId::from(0u32))), sym::warn => Some(Level::Warn), sym::deny => Some(Level::Deny), sym::forbid => Some(Level::Forbid), diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 35e1558600d5b..5e7fc33953af5 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -225,6 +225,7 @@ pub fn explain_lint_level_source( Level::Forbid => "-F", Level::Allow => "-A", Level::ForceWarn => "--force-warn", + Level::Expect(_) => unreachable!("the expect level does not have a commandline flag"), }; let hyphen_case_lint_name = name.replace('_', "-"); if lint_flag_val.as_str() == name { @@ -314,6 +315,16 @@ pub fn struct_lint_level<'s, 'd>( return; } } + (Level::Expect(expect_id), _) => { + // 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 + // need the lint diagnostic to be emitted to `rustc_error::HanderInner`. + // + // 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 + // create a `DiagnosticBuilder` and continue as we would for warnings. + sess.struct_expect("", expect_id) + } (Level::Warn | Level::ForceWarn, Some(span)) => sess.struct_span_warn(span, ""), (Level::Warn | Level::ForceWarn, None) => sess.struct_warn(""), (Level::Deny | Level::Forbid, Some(span)) => { @@ -346,6 +357,17 @@ pub fn struct_lint_level<'s, 'd>( } } + // Lint diagnostics that are covered by the expect level will not be emitted outside + // the compiler. It is therefore not necessary to add any information for the user. + // This will therefore directly call the decorate function which will intern emit + // the `Diagnostic`. + if let Level::Expect(_) = level { + let name = lint.name_lower(); + err.code(DiagnosticId::Lint { name, has_future_breakage, is_force_warn: false }); + decorate(LintDiagnosticBuilder::new(err)); + return; + } + explain_lint_level_source(sess, lint, level, src, &mut err); let name = lint.name_lower(); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index b72be735ce485..edc98abca29dc 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -331,6 +331,9 @@ impl Session { pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } + pub fn struct_expect(&self, msg: &str, id: lint::LintExpectationId) -> DiagnosticBuilder<'_, ()> { + self.diagnostic().struct_expect(msg, id) + } pub fn struct_span_err>( &self, sp: S, From f467a58b7bdc330a5af3962df40ad11fe2f05165 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 6 Aug 2021 23:23:34 +0200 Subject: [PATCH 02/13] Added `unfulfilled_lint_expectations` lint for (RFC-2383) --- compiler/rustc_lint_defs/src/builtin.rs | 34 +++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index adec1a3ab00d6..272913f3f0e02 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -495,6 +495,39 @@ declare_lint! { "unrecognized lint attribute" } +declare_lint! { + /// The `unfulfilled_lint_expectations` lint detects lint trigger expectations + /// that have not been fulfilled. + /// + /// ### Example + /// + /// ```rust + /// #![feature(lint_reasons)] + /// + /// #[expect(unused_variables)] + /// let x = 10; + /// println!("{}", x); + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// It was expected that the marked code would emit a lint. This expectation + /// has not been fulfilled. + /// + /// The `expect` attribute can be removed if this is intended behavior otherwise + /// it should be investigated why the expected lint is no longer issued. + /// + /// Part of RFC 2383. The progress is being tracked in [#54503] + /// + /// [#54503]: https://github.com/rust-lang/rust/issues/54503 + pub UNFULFILLED_LINT_EXPECTATIONS, + Warn, + "unfulfilled lint expectation", + @feature_gate = rustc_span::sym::lint_reasons; +} + declare_lint! { /// The `unused_variables` lint detects variables which are not used in /// any way. @@ -3007,6 +3040,7 @@ declare_lint_pass! { UNUSED_CRATE_DEPENDENCIES, UNUSED_QUALIFICATIONS, UNKNOWN_LINTS, + UNFULFILLED_LINT_EXPECTATIONS, UNUSED_VARIABLES, UNUSED_ASSIGNMENTS, DEAD_CODE, From 2ca9037b6154227306d9b40a87d79e691f1c9126 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 6 Aug 2021 23:28:58 +0200 Subject: [PATCH 03/13] Set `LintExpectationId` in level and collect fulfilled ones (RFC-2383) * Collect lint expectations and set expectation ID in level (RFC-2383) * Collect IDs of fulfilled lint expectations from diagnostics (RFC 2383) --- compiler/rustc_errors/src/diagnostic.rs | 6 +--- compiler/rustc_errors/src/lib.rs | 19 ++++++++++- compiler/rustc_lint/src/levels.rs | 38 +++++++++++++++++----- compiler/rustc_lint_defs/src/lib.rs | 4 +-- compiler/rustc_middle/src/lint.rs | 42 +++++++++++++++++++++++-- compiler/rustc_middle/src/ty/context.rs | 6 +++- compiler/rustc_session/src/session.rs | 6 +++- 7 files changed, 102 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs index 802f25606010f..a59d91ea78900 100644 --- a/compiler/rustc_errors/src/diagnostic.rs +++ b/compiler/rustc_errors/src/diagnostic.rs @@ -133,11 +133,7 @@ impl Diagnostic { | Level::Error { .. } | Level::FailureNote => true, - Level::Warning - | Level::Note - | Level::Help - | Level::Allow - | Level::Expect(_) => false, + Level::Warning | Level::Note | Level::Help | Level::Allow | Level::Expect(_) => false, } } diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 41d2b28599738..83e52e002ae2a 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -451,6 +451,15 @@ struct HandlerInner { deduplicated_warn_count: usize, future_breakage_diagnostics: Vec, + + /// Lint [`Diagnostic`]s can be expected as described in [RFC-2383]. An + /// expected diagnostic will have the level `Expect` which additionally + /// carries the [`LintExpectationId`] of the expectation that can be + /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s + /// that have been marked as fulfilled this way. + /// + /// [RFC-2383]: https://rust-lang.github.io/rfcs/2383-lint-reasons.html + fulfilled_expectations: FxHashSet, } /// A key denoting where from a diagnostic was stashed. @@ -571,6 +580,7 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + fulfilled_expectations: Default::default(), }), } } @@ -912,6 +922,12 @@ impl Handler { pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) { self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs) } + + /// This methods steals all [`LintExpectationId`]s that are stored inside + /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. + pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet { + std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations) + } } impl HandlerInner { @@ -959,7 +975,8 @@ impl HandlerInner { (*TRACK_DIAGNOSTICS)(diagnostic); - if let Level::Expect(_) = diagnostic.level { + if let Level::Expect(expectation_id) = diagnostic.level { + self.fulfilled_expectations.insert(expectation_id); return; } else if diagnostic.level == Allow { return; diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index d7cdb08d81714..a876e35c0a8d7 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -7,17 +7,15 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic}; use rustc_hir as hir; use rustc_hir::{intravisit, HirId}; use rustc_middle::hir::nested_filter; -use rustc_middle::lint::LevelAndSource; -use rustc_middle::lint::LintDiagnosticBuilder; use rustc_middle::lint::{ - struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, LintStackIndex, - COMMAND_LINE, + struct_lint_level, LevelAndSource, LintDiagnosticBuilder, LintExpectation, LintLevelMap, + LintLevelSets, LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE, }; use rustc_middle::ty::query::Providers; use rustc_middle::ty::{RegisteredTools, TyCtxt}; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, - Level, Lint, LintId, + Level, Lint, LintExpectationId, LintId, }; use rustc_session::parse::feature_err; use rustc_session::Session; @@ -44,6 +42,7 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { pub struct LintLevelsBuilder<'s> { sess: &'s Session, + lint_expectations: FxHashMap, sets: LintLevelSets, id_to_set: FxHashMap, cur: LintStackIndex, @@ -66,6 +65,7 @@ impl<'s> LintLevelsBuilder<'s> { ) -> Self { let mut builder = LintLevelsBuilder { sess, + lint_expectations: Default::default(), sets: LintLevelSets::new(), cur: COMMAND_LINE, id_to_set: Default::default(), @@ -231,7 +231,7 @@ impl<'s> LintLevelsBuilder<'s> { let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); for attr in attrs { - let Some(level) = Level::from_symbol(attr.name_or_empty()) else { + let Some(level) = Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()) else { continue }; @@ -476,6 +476,26 @@ impl<'s> LintLevelsBuilder<'s> { } } } + + if !specs.is_empty() { + // Only lints that are currently registered in the lint store + // have been found and added to `specs`. Creating the expectation + // here ensures that it can be fulfilled during this compilation + // session. + if let Level::Expect(expect_id) = level { + let has_lints = specs + .values() + .any(|(lvl, _src)| matches!(lvl, Level::Expect(check_id) if check_id.eq(&expect_id))); + + if has_lints { + let lint = builtin::UNFULFILLED_LINT_EXPECTATIONS; + let (lvl, src) = + self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); + let expectation = LintExpectation::new(reason, attr.span, lvl, src); + self.lint_expectations.insert(expect_id, expectation); + } + } + } } if !is_crate_node { @@ -563,7 +583,11 @@ impl<'s> LintLevelsBuilder<'s> { } pub fn build_map(self) -> LintLevelMap { - LintLevelMap { sets: self.sets, id_to_set: self.id_to_set } + LintLevelMap { + sets: self.sets, + id_to_set: self.id_to_set, + lint_expectations: self.lint_expectations, + } } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e6643cfd544f1..6fca39b2c6bb5 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -133,10 +133,10 @@ impl Level { } /// Converts a symbol to a level. - pub fn from_symbol(x: Symbol) -> Option { + pub fn from_symbol(x: Symbol, possible_lint_expect_id: u32) -> Option { match x { sym::allow => Some(Level::Allow), - sym::expect => Some(Level::Expect(LintExpectationId::from(0u32))), + sym::expect => Some(Level::Expect(LintExpectationId::from(possible_lint_expect_id))), sym::warn => Some(Level::Warn), sym::deny => Some(Level::Deny), sym::forbid => Some(Level::Forbid), diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 5e7fc33953af5..9eb7aca13c0ef 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -6,6 +6,7 @@ use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId}; use rustc_hir::HirId; use rustc_index::vec::IndexVec; use rustc_query_system::ich::StableHashingContext; +use rustc_session::lint::LintExpectationId; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, FutureIncompatibilityReason, Level, Lint, LintId, @@ -153,6 +154,13 @@ impl LintLevelSets { #[derive(Debug)] pub struct LintLevelMap { + /// This is a collection of lint expectations as described in RFC 2383, that + /// can be fulfilled during this compilation session. This means that at least + /// one expected lint is currently registered in the lint store. + /// + /// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect) + /// lint level. + pub lint_expectations: FxHashMap, pub sets: LintLevelSets, pub id_to_set: FxHashMap, } @@ -178,14 +186,42 @@ impl LintLevelMap { impl<'a> HashStable> for LintLevelMap { #[inline] fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) { - let LintLevelMap { ref sets, ref id_to_set } = *self; + let LintLevelMap { ref sets, ref id_to_set, ref lint_expectations } = *self; id_to_set.hash_stable(hcx, hasher); + lint_expectations.hash_stable(hcx, hasher); hcx.while_hashing_spans(true, |hcx| sets.hash_stable(hcx, hasher)) } } +/// This struct represents a lint expectation and holds all required information +/// to emit the `unfulfilled_lint_expectations` lint if it is unfulfilled after +/// the `LateLintPass` has completed. +#[derive(Clone, Debug, HashStable)] +pub struct LintExpectation { + /// The reason for this expectation that can optionally be added as part of + /// the attribute. It will be displayed as part of the lint message. + pub reason: Option, + /// The [`Span`] of the attribute that this expectation originated from. + pub emission_span: Span, + /// The [`Level`] that this lint diagnostic should be emitted if unfulfilled. + pub emission_level: Level, + /// The [`LintLevelSource`] information needed for [`struct_lint_level`]. + pub emission_level_source: LintLevelSource, +} + +impl LintExpectation { + pub fn new( + reason: Option, + attr_span: Span, + emission_level: Level, + emission_level_source: LintLevelSource, + ) -> Self { + Self { reason, emission_span: attr_span, emission_level, emission_level_source } + } +} + pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a, ()>); impl<'a> LintDiagnosticBuilder<'a> { @@ -225,7 +261,9 @@ pub fn explain_lint_level_source( Level::Forbid => "-F", Level::Allow => "-A", Level::ForceWarn => "--force-warn", - Level::Expect(_) => unreachable!("the expect level does not have a commandline flag"), + Level::Expect(_) => { + unreachable!("the expect level does not have a commandline flag") + } }; let hyphen_case_lint_name = name.replace('_', "-"); if lint_flag_val.as_str() == name { diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index f9435682e5394..bd48a9867f9eb 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2755,7 +2755,11 @@ impl<'tcx> TyCtxt<'tcx> { return bound; } - if hir.attrs(id).iter().any(|attr| Level::from_symbol(attr.name_or_empty()).is_some()) { + if hir + .attrs(id) + .iter() + .any(|attr| Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()).is_some()) + { return id; } let next = hir.get_parent_node(id); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index edc98abca29dc..4a7e7e267a2e5 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -331,7 +331,11 @@ impl Session { pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_allow(msg) } - pub fn struct_expect(&self, msg: &str, id: lint::LintExpectationId) -> DiagnosticBuilder<'_, ()> { + pub fn struct_expect( + &self, + msg: &str, + id: lint::LintExpectationId, + ) -> DiagnosticBuilder<'_, ()> { self.diagnostic().struct_expect(msg, id) } pub fn struct_span_err>( From 44cb8fa482abaa567119ceab125498cfeef1171b Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 6 Aug 2021 23:36:33 +0200 Subject: [PATCH 04/13] Check lint expectations and emit lint if unfulfilled (RFC-2383) --- compiler/rustc_lint/src/expect.rs | 48 +++++++++++++++++++++++++++++++ compiler/rustc_lint/src/late.rs | 3 ++ compiler/rustc_lint/src/lib.rs | 1 + 3 files changed, 52 insertions(+) create mode 100644 compiler/rustc_lint/src/expect.rs diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs new file mode 100644 index 0000000000000..a0c8b3501cd56 --- /dev/null +++ b/compiler/rustc_lint/src/expect.rs @@ -0,0 +1,48 @@ +use crate::builtin; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::lint::struct_lint_level; +use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; +use rustc_session::lint::LintExpectationId; +use rustc_span::symbol::sym; +use rustc_span::MultiSpan; + +pub fn check_expectations(tcx: TyCtxt<'_>) { + if !tcx.sess.features_untracked().enabled(sym::lint_reasons) { + return; + } + + let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids(); + let lint_expectations: &FxHashMap = + &tcx.lint_levels(()).lint_expectations; + + for (id, expectation) in lint_expectations { + if fulfilled_expectations.contains(id) { + continue; + } + + emit_unfulfilled_expectation_lint(tcx, expectation); + } +} + +fn emit_unfulfilled_expectation_lint(tcx: TyCtxt<'_>, expectation: &LintExpectation) { + // FIXME The current implementation doesn't cover cases where the + // `unfulfilled_lint_expectations` is actually expected by another lint + // expectation. This can be added here as we have the lint level of this + // expectation, and we can also mark the lint expectation it would fulfill + // as such. This is currently not implemented to get some early feedback + // before diving deeper into this. + struct_lint_level( + tcx.sess, + builtin::UNFULFILLED_LINT_EXPECTATIONS, + expectation.emission_level, + expectation.emission_level_source, + Some(MultiSpan::from_span(expectation.emission_span)), + |diag| { + let mut diag = diag.build("this lint expectation is unfulfilled"); + if let Some(rationale) = expectation.reason { + diag.note(&rationale.as_str()); + } + diag.emit(); + }, + ); +} diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 0ce760b64d9ca..0ac636b878e0d 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -503,4 +503,7 @@ pub fn check_crate<'tcx, T: LateLintPass<'tcx>>( }); }, ); + + // This check has to be run after all lints are done processing for this crate + tcx.sess.time("check_lint_expectations", || crate::expect::check_expectations(tcx)); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 72e1671449f1c..2dc6e9807227e 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -52,6 +52,7 @@ mod context; mod early; mod enum_intrinsics_non_enums; pub mod hidden_unicode_codepoints; +mod expect; mod internal; mod late; mod levels; From 33a5945069e2c7bd3ba8a0dd65b74ebdd234ad7c Mon Sep 17 00:00:00 2001 From: xFrednet Date: Sat, 20 Nov 2021 20:45:27 +0100 Subject: [PATCH 05/13] Make `LintExpectationId` stable between compilation sessions (RFC-2383) --- Cargo.lock | 2 +- compiler/rustc_errors/src/lib.rs | 50 ++++++++++++++- compiler/rustc_lint/src/early.rs | 3 +- compiler/rustc_lint/src/expect.rs | 2 +- compiler/rustc_lint/src/levels.rs | 50 +++++++++++++-- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_lint_defs/Cargo.toml | 2 +- compiler/rustc_lint_defs/src/lib.rs | 82 +++++++++++++++++++------ compiler/rustc_middle/src/lint.rs | 3 +- compiler/rustc_middle/src/ty/context.rs | 14 +++-- 10 files changed, 171 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index bc2c77d9271f4..8ca6f26e326d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3882,7 +3882,7 @@ version = "0.0.0" dependencies = [ "rustc_ast", "rustc_data_structures", - "rustc_index", + "rustc_hir", "rustc_macros", "rustc_serialize", "rustc_span", diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 83e52e002ae2a..0fd057f434b33 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -25,7 +25,7 @@ use Level::*; use emitter::{is_case_difference, Emitter, EmitterWriter}; use registry::Registry; -use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_data_structures::stable_hasher::StableHasher; use rustc_data_structures::sync::{self, Lock, Lrc}; use rustc_data_structures::AtomicRef; @@ -452,7 +452,14 @@ struct HandlerInner { future_breakage_diagnostics: Vec, - /// Lint [`Diagnostic`]s can be expected as described in [RFC-2383]. An + /// Expected [`Diagnostic`]s store a [`LintExpectationId`] as part of + /// the lint level. [`LintExpectationId`]s created early during the compilation + /// (before `HirId`s have been defined) are not stable and can therefore not be + /// stored on disk. This buffer stores these diagnostics until the ID has been + /// replaced by a stable [`LintExpectationId`]. The [`Diagnostic`]s are the + /// submitted for storage and added to the list of fulfilled expectations. + unstable_expect_diagnostics: Vec, + /// expected diagnostic will have the level `Expect` which additionally /// carries the [`LintExpectationId`] of the expectation that can be /// marked as fulfilled. This is a collection of all [`LintExpectationId`]s @@ -580,6 +587,7 @@ impl Handler { emitted_diagnostics: Default::default(), stashed_diagnostics: Default::default(), future_breakage_diagnostics: Vec::new(), + unstable_expect_diagnostics: Vec::new(), fulfilled_expectations: Default::default(), }), } @@ -923,6 +931,28 @@ impl Handler { self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs) } + pub fn update_unstable_expectation_id( + &self, + unstable_to_stable: &FxHashMap, + ) { + let diags = std::mem::take(&mut self.inner.borrow_mut().unstable_expect_diagnostics); + if diags.is_empty() { + return; + } + + let mut inner = self.inner.borrow_mut(); + for mut diag in diags.into_iter() { + if let Some(unstable_id) = diag.level.get_expectation_id() { + if let Some(stable_id) = unstable_to_stable.get(&unstable_id) { + diag.level = Level::Expect(*stable_id); + inner.fulfilled_expectations.insert(*stable_id); + } + } + + (*TRACK_DIAGNOSTICS)(&diag); + } + } + /// This methods steals all [`LintExpectationId`]s that are stored inside /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet { @@ -973,6 +1003,15 @@ impl HandlerInner { return; } + // The `LintExpectationId` can be stable or unstable depending on when it was created. + // Diagnostics created before the definition of `HirId`s are unstable and can not yet + // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by + // a stable one by the `LintLevelsBuilder`. + if let Level::Expect(LintExpectationId::Unstable(_)) = diagnostic.level { + self.unstable_expect_diagnostics.push(diagnostic.clone()); + return; + } + (*TRACK_DIAGNOSTICS)(diagnostic); if let Level::Expect(expectation_id) = diagnostic.level { @@ -1322,6 +1361,13 @@ impl Level { pub fn is_failure_note(&self) -> bool { matches!(*self, FailureNote) } + + pub fn get_expectation_id(&self) -> Option { + match self { + Level::Expect(id) => Some(*id), + _ => None, + } + } } // FIXME(eddyb) this doesn't belong here AFAICT, should be moved to callsite. diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 1b2c88867d49d..e9b7620bf1d7f 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -59,7 +59,8 @@ impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { F: FnOnce(&mut Self), { let is_crate_node = id == ast::CRATE_NODE_ID; - let push = self.context.builder.push(attrs, is_crate_node); + let push = self.context.builder.push(attrs, is_crate_node, None); + self.check_id(id); self.enter_attrs(attrs); f(self); diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index a0c8b3501cd56..49664322a98bf 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -25,7 +25,7 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { } fn emit_unfulfilled_expectation_lint(tcx: TyCtxt<'_>, expectation: &LintExpectation) { - // FIXME The current implementation doesn't cover cases where the + // FIXME: The current implementation doesn't cover cases where the // `unfulfilled_lint_expectations` is actually expected by another lint // expectation. This can be added here as we have the lint level of this // expectation, and we can also mark the lint expectation it would fulfill diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index a876e35c0a8d7..417022bd2bf10 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -32,17 +32,23 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { builder.levels.id_to_set.reserve(krate.owners.len() + 1); - let push = builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true); + let push = + builder.levels.push(tcx.hir().attrs(hir::CRATE_HIR_ID), true, Some(hir::CRATE_HIR_ID)); + builder.levels.register_id(hir::CRATE_HIR_ID); tcx.hir().walk_toplevel_module(&mut builder); builder.levels.pop(push); + builder.levels.update_unstable_expectation_ids(); builder.levels.build_map() } pub struct LintLevelsBuilder<'s> { sess: &'s Session, lint_expectations: FxHashMap, + /// Each expectation has a stable and an unstable identifier. This map + /// is used to map from unstable to stable [`LintExpectationId`]s. + expectation_id_map: FxHashMap, sets: LintLevelSets, id_to_set: FxHashMap, cur: LintStackIndex, @@ -66,6 +72,7 @@ impl<'s> LintLevelsBuilder<'s> { let mut builder = LintLevelsBuilder { sess, lint_expectations: Default::default(), + expectation_id_map: Default::default(), sets: LintLevelSets::new(), cur: COMMAND_LINE, id_to_set: Default::default(), @@ -226,13 +233,26 @@ impl<'s> LintLevelsBuilder<'s> { /// `#[allow]` /// /// Don't forget to call `pop`! - pub(crate) fn push(&mut self, attrs: &[ast::Attribute], is_crate_node: bool) -> BuilderPush { + pub(crate) fn push( + &mut self, + attrs: &[ast::Attribute], + is_crate_node: bool, + source_hir_id: Option, + ) -> BuilderPush { let mut specs = FxHashMap::default(); let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); - for attr in attrs { - let Some(level) = Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()) else { - continue + for (attr_index, attr) in attrs.iter().enumerate() { + let level = match Level::from_symbol(attr.name_or_empty(), || { + LintExpectationId::Unstable(attr.id) + }) { + None => continue, + Some(Level::Expect(unstable_id)) if source_hir_id.is_some() => { + let stable_id = + self.create_stable_id(unstable_id, source_hir_id.unwrap(), attr_index); + Level::Expect(stable_id) + } + Some(lvl) => lvl, }; let Some(mut metas) = attr.meta_item_list() else { @@ -539,6 +559,19 @@ impl<'s> LintLevelsBuilder<'s> { BuilderPush { prev, changed: prev != self.cur } } + fn create_stable_id( + &mut self, + unstable_id: LintExpectationId, + hir_id: HirId, + attr_index: usize, + ) -> LintExpectationId { + let stable_id = LintExpectationId::Stable { hir_id, attr_index }; + + self.expectation_id_map.insert(unstable_id, stable_id); + + stable_id + } + /// Checks if the lint is gated on a feature that is not enabled. fn check_gated_lint(&self, lint_id: LintId, span: Span) { if let Some(feature) = lint_id.lint.feature_gate { @@ -582,6 +615,10 @@ impl<'s> LintLevelsBuilder<'s> { self.id_to_set.insert(id, self.cur); } + fn update_unstable_expectation_ids(&self) { + self.sess.diagnostic().update_unstable_expectation_id(&self.expectation_id_map); + } + pub fn build_map(self) -> LintLevelMap { LintLevelMap { sets: self.sets, @@ -603,7 +640,8 @@ impl LintLevelMapBuilder<'_> { { let is_crate_hir = id == hir::CRATE_HIR_ID; let attrs = self.tcx.hir().attrs(id); - let push = self.levels.push(attrs, is_crate_hir); + let push = self.levels.push(attrs, is_crate_hir, Some(id)); + if push.changed { self.levels.register_id(id); } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2dc6e9807227e..18f229564c2a3 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -51,8 +51,8 @@ pub mod builtin; mod context; mod early; mod enum_intrinsics_non_enums; -pub mod hidden_unicode_codepoints; mod expect; +pub mod hidden_unicode_codepoints; mod internal; mod late; mod levels; diff --git a/compiler/rustc_lint_defs/Cargo.toml b/compiler/rustc_lint_defs/Cargo.toml index c37e45b46ce96..8acf7943de9cf 100644 --- a/compiler/rustc_lint_defs/Cargo.toml +++ b/compiler/rustc_lint_defs/Cargo.toml @@ -10,4 +10,4 @@ rustc_span = { path = "../rustc_span" } rustc_serialize = { path = "../rustc_serialize" } rustc_macros = { path = "../rustc_macros" } rustc_target = { path = "../rustc_target" } -rustc_index = { path = "../rustc_index" } +rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 6fca39b2c6bb5..5e3b6889fc667 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -5,7 +5,9 @@ extern crate rustc_macros; pub use self::Level::*; use rustc_ast::node_id::{NodeId, NodeMap}; +use rustc_ast::AttrId; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; +use rustc_hir::HirId; use rustc_serialize::json::Json; use rustc_span::edition::Edition; use rustc_span::{sym, symbol::Ident, MultiSpan, Span, Symbol}; @@ -48,29 +50,70 @@ pub enum Applicability { Unspecified, } -rustc_index::newtype_index! { - /// FIXME: The lint expectation ID is currently a simple copy of the `AttrId` - /// that the expectation originated from. In the future it should be generated - /// by other means. This is for one to keep the IDs independent of each other - /// and also to ensure that it is actually stable between compilation sessions. - /// (The `AttrId` for instance, is not stable). - /// - /// Additionally, it would be nice if this generation could be moved into - /// [`Level::from_symbol`] to have it all contained in one module and to - /// make it simpler to use. - pub struct LintExpectationId { - DEBUG_FORMAT = "LintExpectationId({})" +/// Each lint expectation has a `LintExpectationId` assigned by the +/// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`]. Expected +/// [`Diagnostic`][`rustc_errors::Diagnostic`]s get the lint level `Expect` which +/// stores the `LintExpectationId` to match it with the actual expectation later on. +/// +/// The `LintExpectationId` has to be has stable between compilations, as diagnostic +/// instances might be loaded from cache. Lint messages can be emitted during an +/// `EarlyLintPass` operating on the AST and during a `LateLintPass` traversing the +/// HIR tree. The AST doesn't have enough information to create a stable id. The +/// `LintExpectationId` will instead store the [`AttrId`] defining the expectation. +/// These `LintExpectationId` will be updated to use the stable [`HirId`] once the +/// AST has been lowered. The transformation is done by the +/// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`] +#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)] +pub enum LintExpectationId { + /// Used for lints emitted during the `EarlyLintPass`. This id is not + /// has stable and should not be cached. + Unstable(AttrId), + /// The [`HirId`] that the lint expectation is attached to. This id is + /// stable and can be cached. The additional index ensures that nodes with + /// several expectations can correctly match diagnostics to the individual + /// expectation. + Stable { hir_id: HirId, attr_index: usize }, +} + +impl LintExpectationId { + pub fn is_stable(&self) -> bool { + match self { + LintExpectationId::Unstable(_) => false, + LintExpectationId::Stable { .. } => true, + } } } -rustc_data_structures::impl_stable_hash_via_hash!(LintExpectationId); +impl HashStable for LintExpectationId { + #[inline] + fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { + match self { + LintExpectationId::Unstable(_) => { + unreachable!( + "HashStable should never be called for an unstable `LintExpectationId`" + ) + } + LintExpectationId::Stable { hir_id, attr_index } => { + hir_id.hash_stable(hcx, hasher); + attr_index.hash_stable(hcx, hasher); + } + } + } +} -impl ToStableHashKey for LintExpectationId { - type KeyType = u32; +impl ToStableHashKey for LintExpectationId { + type KeyType = (HirId, usize); #[inline] fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { - self.as_u32() + match self { + LintExpectationId::Unstable(_) => { + unreachable!( + "HashStable should never be called for an unstable `LintExpectationId`" + ) + } + LintExpectationId::Stable { hir_id, attr_index } => (*hir_id, *attr_index), + } } } @@ -133,10 +176,13 @@ impl Level { } /// Converts a symbol to a level. - pub fn from_symbol(x: Symbol, possible_lint_expect_id: u32) -> Option { + pub fn from_symbol(x: Symbol, create_expectation_id: F) -> Option + where + F: FnOnce() -> LintExpectationId, + { match x { sym::allow => Some(Level::Allow), - sym::expect => Some(Level::Expect(LintExpectationId::from(possible_lint_expect_id))), + sym::expect => Some(Level::Expect(create_expectation_id())), sym::warn => Some(Level::Warn), sym::deny => Some(Level::Deny), sym::forbid => Some(Level::Forbid), diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 9eb7aca13c0ef..f680843f9e4e0 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -6,10 +6,9 @@ use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId}; use rustc_hir::HirId; use rustc_index::vec::IndexVec; use rustc_query_system::ich::StableHashingContext; -use rustc_session::lint::LintExpectationId; use rustc_session::lint::{ builtin::{self, FORBIDDEN_LINT_GROUPS}, - FutureIncompatibilityReason, Level, Lint, LintId, + FutureIncompatibilityReason, Level, Lint, LintExpectationId, LintId, }; use rustc_session::{DiagnosticMessageId, Session}; use rustc_span::hygiene::MacroKind; diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index bd48a9867f9eb..5165193ab540e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -49,7 +49,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_query_system::ich::{NodeIdHashingMode, StableHashingContext}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; -use rustc_session::lint::{Level, Lint}; +use rustc_session::lint::{Level, Lint, LintExpectationId}; use rustc_session::Limit; use rustc_session::Session; use rustc_span::def_id::{DefPathHash, StableCrateId}; @@ -2755,11 +2755,13 @@ impl<'tcx> TyCtxt<'tcx> { return bound; } - if hir - .attrs(id) - .iter() - .any(|attr| Level::from_symbol(attr.name_or_empty(), attr.id.as_u32()).is_some()) - { + if hir.attrs(id).iter().enumerate().any(|(attr_index, attr)| { + Level::from_symbol(attr.name_or_empty(), || LintExpectationId::Stable { + hir_id: id, + attr_index, + }) + .is_some() + }) { return id; } let next = hir.get_parent_node(id); From a9bf9eaef5165067414b33777a2c924e42aab5aa Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 6 Aug 2021 23:38:09 +0200 Subject: [PATCH 06/13] Add UI tests for the `expect` attribute (RFC-2383) * Add UI tests with macros for the `expect` attribute (RFC-2383) * Addressed review comments - mostly UI test updates (RFC-2383) * Documented lint level attribute on macro not working bug (RFC-2383) See `rust#87391` --- compiler/rustc_lint/src/expect.rs | 6 +- .../catch_multiple_lint_triggers.rs | 45 ++++++++++++++ .../crate_level_expect.rs | 15 +++++ .../crate_level_expect.stderr | 10 +++ .../expect_inside_macro.rs | 16 +++++ .../expect_lint_from_macro.rs | 42 +++++++++++++ .../expect_lint_from_macro.stderr | 29 +++++++++ .../expect_missing_feature_gate.rs | 9 +++ .../expect_missing_feature_gate.stderr | 12 ++++ .../expect_multiple_lints.rs | 31 ++++++++++ .../expect_nested_lint_levels.rs | 43 +++++++++++++ .../expect_nested_lint_levels.stderr | 40 ++++++++++++ .../expect_with_reason.rs | 11 ++++ .../expect_with_reason.stderr | 11 ++++ .../fulfilled_expectation_early_lints.rs | 23 +++++++ .../fulfilled_expectation_late_lints.rs | 61 +++++++++++++++++++ .../multiple_expect_attrs.rs | 14 +++++ .../multiple_expect_attrs.stderr | 10 +++ 18 files changed, 424 insertions(+), 4 deletions(-) create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/catch_multiple_lint_triggers.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_inside_macro.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.stderr create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_early_lints.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 49664322a98bf..db0756f5f54e2 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -16,11 +16,9 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { &tcx.lint_levels(()).lint_expectations; for (id, expectation) in lint_expectations { - if fulfilled_expectations.contains(id) { - continue; + if !fulfilled_expectations.contains(id) { + emit_unfulfilled_expectation_lint(tcx, expectation); } - - emit_unfulfilled_expectation_lint(tcx, expectation); } } diff --git a/src/test/ui/lint/rfc-2383-lint-reason/catch_multiple_lint_triggers.rs b/src/test/ui/lint/rfc-2383-lint-reason/catch_multiple_lint_triggers.rs new file mode 100644 index 0000000000000..6b255b799b7ec --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/catch_multiple_lint_triggers.rs @@ -0,0 +1,45 @@ +// check-pass + +#![feature(lint_reasons)] + +#![warn(unused)] + +// This expect attribute should catch all lint triggers +#[expect(unused_variables)] +fn check_multiple_lints_1() { + let value_i = 0xff00ff; + let value_ii = 0xff00ff; + let value_iii = 0xff00ff; + let value_iiii = 0xff00ff; + let value_iiiii = 0xff00ff; +} + +// This expect attribute should catch all lint triggers +#[expect(unused_mut)] +fn check_multiple_lints_2() { + let mut a = 0xa; + let mut b = 0xb; + let mut c = 0xc; + println!("The ABC goes as: {:#x} {:#x} {:#x}", a, b, c); +} + +// This expect attribute should catch all lint triggers +#[expect(while_true)] +fn check_multiple_lints_3() { + // `while_true` is an early lint + while true {} + + while true {} + + while true {} + + while true {} + + while true {} +} + +fn main() { + check_multiple_lints_1(); + check_multiple_lints_2(); + check_multiple_lints_3(); +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.rs b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.rs new file mode 100644 index 0000000000000..9f591ba985232 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.rs @@ -0,0 +1,15 @@ +// check-pass + +#![feature(lint_reasons)] + +#![warn(unused)] + +#![expect(unused_mut)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default + +#![expect(unused_variables)] + +fn main() { + let x = 0; +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr new file mode 100644 index 0000000000000..757e6471c0616 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr @@ -0,0 +1,10 @@ +warning: this lint expectation is unfulfilled + --> $DIR/crate_level_expect.rs:7:1 + | +LL | #![expect(unused_mut)] + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_inside_macro.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_inside_macro.rs new file mode 100644 index 0000000000000..b95815bc50b98 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_inside_macro.rs @@ -0,0 +1,16 @@ +// check-pass + +#![feature(lint_reasons)] + +#![warn(unused)] + +macro_rules! expect_inside_macro { + () => { + #[expect(unused_variables)] + let x = 0; + }; +} + +fn main() { + expect_inside_macro!(); +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs new file mode 100644 index 0000000000000..07c60fa0c325b --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.rs @@ -0,0 +1,42 @@ +// check-pass + +#![feature(lint_reasons)] + +#![warn(unused_variables)] + +macro_rules! trigger_unused_variables_macro { + () => { + let x = 0; + //~^ WARNING unused variable: `x` [unused_variables] + //~| WARNING unused variable: `x` [unused_variables] + }; +} + +pub fn check_macro() { + // This should trigger the `unused_variables` from inside the macro + trigger_unused_variables_macro!(); +} + +// This should be fulfilled by the macro +#[expect(unused_variables)] +pub fn check_expect_on_item() { + trigger_unused_variables_macro!(); +} + +pub fn check_expect_on_macro() { + // This should be fulfilled by the macro + #[expect(unused_variables)] + trigger_unused_variables_macro!(); + + // FIXME: Lint attributes currently don't work directly on macros, and + // therefore also doesn't work for the new `expect` attribute. This bug + // is being tracked in rust#87391. The test will until then produce two + // warnings about the unused variable x. + // + // The expectation is still marked as fulfilled. I'm not totally why but + // my guess is that this will remain working when rust#87391 has been fixed. +} + +fn main() { + +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr new file mode 100644 index 0000000000000..817e16fdcaa06 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_lint_from_macro.stderr @@ -0,0 +1,29 @@ +warning: unused variable: `x` + --> $DIR/expect_lint_from_macro.rs:9:13 + | +LL | let x = 0; + | ^ help: if this is intentional, prefix it with an underscore: `_x` +... +LL | trigger_unused_variables_macro!(); + | --------------------------------- in this macro invocation + | +note: the lint level is defined here + --> $DIR/expect_lint_from_macro.rs:5:9 + | +LL | #![warn(unused_variables)] + | ^^^^^^^^^^^^^^^^ + = note: this warning originates in the macro `trigger_unused_variables_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: unused variable: `x` + --> $DIR/expect_lint_from_macro.rs:9:13 + | +LL | let x = 0; + | ^ help: if this is intentional, prefix it with an underscore: `_x` +... +LL | trigger_unused_variables_macro!(); + | --------------------------------- in this macro invocation + | + = note: this warning originates in the macro `trigger_unused_variables_macro` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 2 warnings emitted + diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.rs new file mode 100644 index 0000000000000..928e161061432 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.rs @@ -0,0 +1,9 @@ +// should error due to missing feature gate. + +#![warn(unused)] + +#[expect(unused)] +//~^ ERROR: the `#[expect]` attribute is an experimental feature [E0658] +fn main() { + let x = 1; +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.stderr new file mode 100644 index 0000000000000..b5601cf9e6594 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_missing_feature_gate.stderr @@ -0,0 +1,12 @@ +error[E0658]: the `#[expect]` attribute is an experimental feature + --> $DIR/expect_missing_feature_gate.rs:5:1 + | +LL | #[expect(unused)] + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #54503 for more information + = help: add `#![feature(lint_reasons)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs new file mode 100644 index 0000000000000..943150d2dbdf4 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs @@ -0,0 +1,31 @@ +// check-pass + +#![feature(lint_reasons)] + +#![warn(unused)] + +#[expect(unused_variables, unused_mut, while_true)] +fn check_multiple_lints_1() { + // This only trigger `unused_variables` + let who_am_i = 666; +} + +#[expect(unused_variables, unused_mut, while_true)] +fn check_multiple_lints_2() { + // This only triggers `unused_mut` + let mut x = 0; + println!("I use x: {}", x); +} + + +#[expect(unused_variables, unused_mut, while_true)] +fn check_multiple_lints_3() { + // This only triggers `while_true` which is also an early lint + while true {} +} + +fn main() { + check_multiple_lints_1(); + check_multiple_lints_2(); + check_multiple_lints_3(); +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs new file mode 100644 index 0000000000000..f5f0a0bc591d3 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs @@ -0,0 +1,43 @@ +// check-pass +// ignore-tidy-linelength + +#![feature(lint_reasons)] +#![warn(unused_mut)] + +#[expect( + unused_mut, + reason = "this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered" +)] +//~^^^^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered +mod foo { + fn bar() { + #[allow( + unused_mut, + reason = "this overrides the previous `expect` lint level and allows the `unused_mut` lint here" + )] + let mut v = 0; + } +} + +#[expect( + unused_mut, + reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered" +)] +//~^^^^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default +//~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered +mod oof { + #[warn( + unused_mut, + //~^ NOTE the lint level is defined here + reason = "this overrides the previous `expect` lint level and warns about the `unused_mut` lint here" + )] + fn bar() { + let mut v = 0; + //~^ WARNING variable does not need to be mutable [unused_mut] + //~| NOTE this overrides the previous `expect` lint level and warns about the `unused_mut` lint here + } +} + +fn main() {} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr new file mode 100644 index 0000000000000..ed3e99d338683 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr @@ -0,0 +1,40 @@ +warning: variable does not need to be mutable + --> $DIR/expect_nested_lint_levels.rs:37:13 + | +LL | let mut v = 0; + | ----^ + | | + | help: remove this `mut` + | + = note: this overrides the previous `expect` lint level and warns about the `unused_mut` lint here +note: the lint level is defined here + --> $DIR/expect_nested_lint_levels.rs:32:9 + | +LL | unused_mut, + | ^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_nested_lint_levels.rs:23:1 + | +LL | / #[expect( +LL | | unused_mut, +LL | | reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered" +LL | | )] + | |__^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + = note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered + +warning: this lint expectation is unfulfilled + --> $DIR/expect_nested_lint_levels.rs:7:1 + | +LL | / #[expect( +LL | | unused_mut, +LL | | reason = "this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered" +LL | | )] + | |__^ + | + = note: this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered + +warning: 3 warnings emitted + diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.rs new file mode 100644 index 0000000000000..b4183d982118e --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.rs @@ -0,0 +1,11 @@ +// check-pass + +#![feature(lint_reasons)] +#![warn(unused)] + +#![expect(unused_variables, reason = "")] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default +//~| NOTE + +fn main() {} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr new file mode 100644 index 0000000000000..f1d90978f831e --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr @@ -0,0 +1,11 @@ +warning: this lint expectation is unfulfilled + --> $DIR/expect_with_reason.rs:6:1 + | +LL | #![expect(unused_variables, reason = "")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + = note: + +warning: 1 warning emitted + diff --git a/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_early_lints.rs b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_early_lints.rs new file mode 100644 index 0000000000000..6624b930e5e70 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_early_lints.rs @@ -0,0 +1,23 @@ +// check-pass + +#![feature(lint_reasons)] + +fn expect_early_pass_lints() { + #[expect(while_true)] + while true { + println!("I never stop") + } + + #[expect(unused_doc_comments)] + /// This comment triggers the `unused_doc_comments` lint + let _sheep = "wolf"; + + let x = 123; + #[expect(ellipsis_inclusive_range_patterns)] + match x { + 0...100 => {} + _ => {} + } +} + +fn main() {} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs new file mode 100644 index 0000000000000..b8182b5b620d1 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs @@ -0,0 +1,61 @@ +// check-pass + +#![feature(lint_reasons)] +#![warn(unused)] + +#[expect(unused_variables)] +fn check_specific_lint() { + let x = 2; +} + +#[expect(unused)] +fn check_lint_group() { + let x = 15; +} + +#[expect(unused_variables)] +fn check_multiple_lint_emissions() { + let r = 1; + let u = 8; + let s = 2; + let t = 9; +} + +mod check_multiple_expected_lints { + #[expect(unused_variables, unused_mut)] + pub fn check_lint_1() { + // unused_variables should fulfill the expectation + let c = 17; + } + + #[expect(unused_variables, unused_mut)] + pub fn check_lint_2() { + // unused_mut should fulfill the expectation + let mut c = 17; + let _ = c; + } +} + +mod check_fulfilled_expect_in_macro { + macro_rules! expect_inside_macro { + () => { + #[expect(unused_variables)] + let x = 0; + }; + } + + pub fn check_macro() { + expect_inside_macro!(); + } +} + +fn main() { + check_specific_lint(); + check_lint_group(); + check_multiple_lint_emissions(); + + check_multiple_expected_lints::check_lint_1(); + check_multiple_expected_lints::check_lint_2(); + + check_fulfilled_expect_in_macro::check_macro(); +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.rs b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.rs new file mode 100644 index 0000000000000..98080b4e8224f --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.rs @@ -0,0 +1,14 @@ +// check-pass + +#![feature(lint_reasons)] +#![warn(unused)] + +#[warn(unused_variables)] +#[expect(unused_variables)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default +#[allow(unused_variables)] +#[expect(unused_variables)] // Only this expectation will be fulfilled +fn main() { + let x = 2; +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr new file mode 100644 index 0000000000000..79657eb539395 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr @@ -0,0 +1,10 @@ +warning: this lint expectation is unfulfilled + --> $DIR/multiple_expect_attrs.rs:7:1 + | +LL | #[expect(unused_variables)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + +warning: 1 warning emitted + From aa2a0a83d9aa241bbce62797b1c2140c79389ef5 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 24 Nov 2021 21:57:38 +0100 Subject: [PATCH 07/13] Expect each lint in attribute individually (RFC-2383) --- compiler/rustc_errors/src/lib.rs | 16 ++-- compiler/rustc_lint/src/levels.rs | 88 +++++++++++++------ compiler/rustc_lint_defs/src/lib.rs | 60 ++++++++----- compiler/rustc_middle/src/ty/context.rs | 10 +-- .../crate_level_expect.stderr | 4 +- .../expect_multiple_lints.rs | 29 +++++- .../expect_multiple_lints.stderr | 52 +++++++++++ .../expect_nested_lint_levels.rs | 10 +-- .../expect_nested_lint_levels.stderr | 18 ++-- .../expect_with_reason.stderr | 4 +- .../fulfilled_expectation_late_lints.rs | 18 ---- .../multiple_expect_attrs.stderr | 4 +- 12 files changed, 210 insertions(+), 103 deletions(-) create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 0fd057f434b33..b53208a6e5d1c 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -942,10 +942,16 @@ impl Handler { let mut inner = self.inner.borrow_mut(); for mut diag in diags.into_iter() { - if let Some(unstable_id) = diag.level.get_expectation_id() { - if let Some(stable_id) = unstable_to_stable.get(&unstable_id) { - diag.level = Level::Expect(*stable_id); - inner.fulfilled_expectations.insert(*stable_id); + if let Some(mut unstable_id) = diag.level.get_expectation_id() { + let lint_index = unstable_id.get_lint_index(); + + // The unstable to stable map only maps the unstable it to a stable id + // the lint index is manually transferred here. + unstable_id.set_lint_index(None); + if let Some(mut stable_id) = unstable_to_stable.get(&unstable_id).map(|id| *id) { + stable_id.set_lint_index(lint_index); + diag.level = Level::Expect(stable_id); + inner.fulfilled_expectations.insert(stable_id); } } @@ -1007,7 +1013,7 @@ impl HandlerInner { // Diagnostics created before the definition of `HirId`s are unstable and can not yet // be stored. Instead, they are buffered until the `LintExpectationId` is replaced by // a stable one by the `LintLevelsBuilder`. - if let Level::Expect(LintExpectationId::Unstable(_)) = diagnostic.level { + if let Level::Expect(LintExpectationId::Unstable { .. }) = diagnostic.level { self.unstable_expect_diagnostics.push(diagnostic.clone()); return; } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 417022bd2bf10..01454d2fbac00 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -243,9 +243,7 @@ impl<'s> LintLevelsBuilder<'s> { let sess = self.sess; let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input"); for (attr_index, attr) in attrs.iter().enumerate() { - let level = match Level::from_symbol(attr.name_or_empty(), || { - LintExpectationId::Unstable(attr.id) - }) { + let level = match Level::from_attr(attr) { None => continue, Some(Level::Expect(unstable_id)) if source_hir_id.is_some() => { let stable_id = @@ -305,9 +303,23 @@ impl<'s> LintLevelsBuilder<'s> { } } - for li in metas { + let (unfulfilled_lint_lvl, unfulfilled_lint_src) = self.sets.get_lint_level( + builtin::UNFULFILLED_LINT_EXPECTATIONS, + self.cur, + Some(&specs), + &sess, + ); + for (lint_index, li) in metas.iter_mut().enumerate() { + let level = match level { + Level::Expect(mut id) => { + id.set_lint_index(Some(lint_index)); + Level::Expect(id) + } + level => level, + }; + let sp = li.span(); - let mut meta_item = match li { + let meta_item = match li { ast::NestedMetaItem::MetaItem(meta_item) if meta_item.is_word() => meta_item, _ => { let mut err = bad_attr(sp); @@ -347,6 +359,17 @@ impl<'s> LintLevelsBuilder<'s> { self.check_gated_lint(id, attr.span); self.insert_spec(&mut specs, id, (level, src)); } + if let Level::Expect(expect_id) = level { + self.lint_expectations.insert( + expect_id, + LintExpectation::new( + reason, + sp, + unfulfilled_lint_lvl, + unfulfilled_lint_src, + ), + ); + } } CheckLintNameResult::Tool(result) => { @@ -362,6 +385,17 @@ impl<'s> LintLevelsBuilder<'s> { for id in ids { self.insert_spec(&mut specs, *id, (level, src)); } + if let Level::Expect(expect_id) = level { + self.lint_expectations.insert( + expect_id, + LintExpectation::new( + reason, + sp, + unfulfilled_lint_lvl, + unfulfilled_lint_src, + ), + ); + } } Err((Some(ids), ref new_lint_name)) => { let lint = builtin::RENAMED_AND_REMOVED_LINTS; @@ -398,6 +432,17 @@ impl<'s> LintLevelsBuilder<'s> { for id in ids { self.insert_spec(&mut specs, *id, (level, src)); } + if let Level::Expect(expect_id) = level { + self.lint_expectations.insert( + expect_id, + LintExpectation::new( + reason, + sp, + unfulfilled_lint_lvl, + unfulfilled_lint_src, + ), + ); + } } Err((None, _)) => { // If Tool(Err(None, _)) is returned, then either the lint does not @@ -491,31 +536,22 @@ impl<'s> LintLevelsBuilder<'s> { self.check_gated_lint(id, attr.span); self.insert_spec(&mut specs, id, (level, src)); } + if let Level::Expect(expect_id) = level { + self.lint_expectations.insert( + expect_id, + LintExpectation::new( + reason, + sp, + unfulfilled_lint_lvl, + unfulfilled_lint_src, + ), + ); + } } else { panic!("renamed lint does not exist: {}", new_name); } } } - - if !specs.is_empty() { - // Only lints that are currently registered in the lint store - // have been found and added to `specs`. Creating the expectation - // here ensures that it can be fulfilled during this compilation - // session. - if let Level::Expect(expect_id) = level { - let has_lints = specs - .values() - .any(|(lvl, _src)| matches!(lvl, Level::Expect(check_id) if check_id.eq(&expect_id))); - - if has_lints { - let lint = builtin::UNFULFILLED_LINT_EXPECTATIONS; - let (lvl, src) = - self.sets.get_lint_level(lint, self.cur, Some(&specs), &sess); - let expectation = LintExpectation::new(reason, attr.span, lvl, src); - self.lint_expectations.insert(expect_id, expectation); - } - } - } } if !is_crate_node { @@ -565,7 +601,7 @@ impl<'s> LintLevelsBuilder<'s> { hir_id: HirId, attr_index: usize, ) -> LintExpectationId { - let stable_id = LintExpectationId::Stable { hir_id, attr_index }; + let stable_id = LintExpectationId::Stable { hir_id, attr_index, lint_index: None }; self.expectation_id_map.insert(unstable_id, stable_id); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 5e3b6889fc667..e350eeea6c850 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -5,7 +5,7 @@ extern crate rustc_macros; pub use self::Level::*; use rustc_ast::node_id::{NodeId, NodeMap}; -use rustc_ast::AttrId; +use rustc_ast::{AttrId, Attribute}; use rustc_data_structures::stable_hasher::{HashStable, StableHasher, ToStableHashKey}; use rustc_hir::HirId; use rustc_serialize::json::Json; @@ -63,56 +63,72 @@ pub enum Applicability { /// These `LintExpectationId` will be updated to use the stable [`HirId`] once the /// AST has been lowered. The transformation is done by the /// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`] +/// +/// Each lint inside the `expect` attribute is tracked individually, the `lint_index` +/// identifies the lint inside the attribute and ensures that the IDs are unique. #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)] pub enum LintExpectationId { /// Used for lints emitted during the `EarlyLintPass`. This id is not /// has stable and should not be cached. - Unstable(AttrId), + Unstable { attr_id: AttrId, lint_index: Option }, /// The [`HirId`] that the lint expectation is attached to. This id is /// stable and can be cached. The additional index ensures that nodes with /// several expectations can correctly match diagnostics to the individual /// expectation. - Stable { hir_id: HirId, attr_index: usize }, + Stable { hir_id: HirId, attr_index: usize, lint_index: Option }, } impl LintExpectationId { pub fn is_stable(&self) -> bool { match self { - LintExpectationId::Unstable(_) => false, + LintExpectationId::Unstable { .. } => false, LintExpectationId::Stable { .. } => true, } } + + 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 { ref mut lint_index, .. } + | LintExpectationId::Stable { ref mut lint_index, .. }) = self; + + *lint_index = new_lint_index + } } impl HashStable for LintExpectationId { #[inline] fn hash_stable(&self, hcx: &mut HCX, hasher: &mut StableHasher) { match self { - LintExpectationId::Unstable(_) => { - unreachable!( - "HashStable should never be called for an unstable `LintExpectationId`" - ) - } - LintExpectationId::Stable { hir_id, attr_index } => { + LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { hir_id.hash_stable(hcx, hasher); attr_index.hash_stable(hcx, hasher); + lint_index.hash_stable(hcx, hasher); + } + _ => { + unreachable!("HashStable should only be called for a filled `LintExpectationId`") } } } } impl ToStableHashKey for LintExpectationId { - type KeyType = (HirId, usize); + type KeyType = (HirId, usize, usize); #[inline] fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { match self { - LintExpectationId::Unstable(_) => { - unreachable!( - "HashStable should never be called for an unstable `LintExpectationId`" - ) + LintExpectationId::Stable { hir_id, attr_index, lint_index: Some(lint_index) } => { + (*hir_id, *attr_index, *lint_index) + } + _ => { + unreachable!("HashStable should only be called for a filled `LintExpectationId`") } - LintExpectationId::Stable { hir_id, attr_index } => (*hir_id, *attr_index), } } } @@ -176,13 +192,13 @@ impl Level { } /// Converts a symbol to a level. - pub fn from_symbol(x: Symbol, create_expectation_id: F) -> Option - where - F: FnOnce() -> LintExpectationId, - { - match x { + pub fn from_attr(attr: &Attribute) -> Option { + match attr.name_or_empty() { sym::allow => Some(Level::Allow), - sym::expect => Some(Level::Expect(create_expectation_id())), + sym::expect => Some(Level::Expect(LintExpectationId::Unstable { + attr_id: attr.id, + lint_index: None, + })), sym::warn => Some(Level::Warn), sym::deny => Some(Level::Deny), sym::forbid => Some(Level::Forbid), diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 5165193ab540e..0a1581cba5963 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -49,7 +49,7 @@ use rustc_middle::mir::FakeReadCause; use rustc_query_system::ich::{NodeIdHashingMode, StableHashingContext}; use rustc_serialize::opaque::{FileEncodeResult, FileEncoder}; use rustc_session::config::{BorrowckMode, CrateType, OutputFilenames}; -use rustc_session::lint::{Level, Lint, LintExpectationId}; +use rustc_session::lint::{Level, Lint}; use rustc_session::Limit; use rustc_session::Session; use rustc_span::def_id::{DefPathHash, StableCrateId}; @@ -2755,13 +2755,7 @@ impl<'tcx> TyCtxt<'tcx> { return bound; } - if hir.attrs(id).iter().enumerate().any(|(attr_index, attr)| { - Level::from_symbol(attr.name_or_empty(), || LintExpectationId::Stable { - hir_id: id, - attr_index, - }) - .is_some() - }) { + if hir.attrs(id).iter().any(|attr| Level::from_attr(attr).is_some()) { return id; } let next = hir.get_parent_node(id); diff --git a/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr index 757e6471c0616..7237f6fb6bba0 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/crate_level_expect.stderr @@ -1,8 +1,8 @@ warning: this lint expectation is unfulfilled - --> $DIR/crate_level_expect.rs:7:1 + --> $DIR/crate_level_expect.rs:7:11 | LL | #![expect(unused_mut)] - | ^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ | = note: `#[warn(unfulfilled_lint_expectations)]` on by default diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs index 943150d2dbdf4..dc9a719a3f7e7 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.rs @@ -4,28 +4,55 @@ #![warn(unused)] +// The warnings are not double triggers, they identify different unfulfilled lint +// expectations one for each listed lint. + #[expect(unused_variables, unused_mut, while_true)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default +//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] fn check_multiple_lints_1() { // This only trigger `unused_variables` let who_am_i = 666; } #[expect(unused_variables, unused_mut, while_true)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] fn check_multiple_lints_2() { // This only triggers `unused_mut` let mut x = 0; println!("I use x: {}", x); } - #[expect(unused_variables, unused_mut, while_true)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +//~| WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] fn check_multiple_lints_3() { // This only triggers `while_true` which is also an early lint while true {} } +#[expect(unused, while_true)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +fn check_multiple_lints_with_lint_group_1() { + let who_am_i = 666; + + let mut x = 0; + println!("I use x: {}", x); +} + +#[expect(unused, while_true)] +//~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] +fn check_multiple_lints_with_lint_group_2() { + while true {} +} + fn main() { check_multiple_lints_1(); check_multiple_lints_2(); check_multiple_lints_3(); + + check_multiple_lints_with_lint_group_1(); + check_multiple_lints_with_lint_group_2(); } diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr new file mode 100644 index 0000000000000..25299aa7ec2fc --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr @@ -0,0 +1,52 @@ +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:10:28 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^ + | + = note: `#[warn(unfulfilled_lint_expectations)]` on by default + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:28:10 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:19:10 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:28:28 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:36:18 + | +LL | #[expect(unused, while_true)] + | ^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:19:40 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:45:10 + | +LL | #[expect(unused, while_true)] + | ^^^^^^ + +warning: this lint expectation is unfulfilled + --> $DIR/expect_multiple_lints.rs:10:40 + | +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^ + +warning: 8 warnings emitted + diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs index f5f0a0bc591d3..9c3d1fe6f0f30 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs @@ -6,10 +6,10 @@ #[expect( unused_mut, + //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] + //~| NOTE this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered reason = "this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered" )] -//~^^^^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] -//~| NOTE this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered mod foo { fn bar() { #[allow( @@ -22,11 +22,11 @@ mod foo { #[expect( unused_mut, + //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] + //~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default + //~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered" )] -//~^^^^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] -//~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default -//~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered mod oof { #[warn( unused_mut, diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr index ed3e99d338683..b3a227f0d3d8e 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr @@ -14,25 +14,19 @@ LL | unused_mut, | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:23:1 + --> $DIR/expect_nested_lint_levels.rs:24:5 | -LL | / #[expect( -LL | | unused_mut, -LL | | reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered" -LL | | )] - | |__^ +LL | unused_mut, + | ^^^^^^^^^^ | = note: `#[warn(unfulfilled_lint_expectations)]` on by default = note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:7:1 + --> $DIR/expect_nested_lint_levels.rs:8:5 | -LL | / #[expect( -LL | | unused_mut, -LL | | reason = "this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered" -LL | | )] - | |__^ +LL | unused_mut, + | ^^^^^^^^^^ | = note: this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr index f1d90978f831e..82c1a4c08bb39 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_reason.stderr @@ -1,8 +1,8 @@ warning: this lint expectation is unfulfilled - --> $DIR/expect_with_reason.rs:6:1 + --> $DIR/expect_with_reason.rs:6:11 | LL | #![expect(unused_variables, reason = "")] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: `#[warn(unfulfilled_lint_expectations)]` on by default = note: diff --git a/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs index b8182b5b620d1..5d928b3cab3fe 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs +++ b/src/test/ui/lint/rfc-2383-lint-reason/fulfilled_expectation_late_lints.rs @@ -21,21 +21,6 @@ fn check_multiple_lint_emissions() { let t = 9; } -mod check_multiple_expected_lints { - #[expect(unused_variables, unused_mut)] - pub fn check_lint_1() { - // unused_variables should fulfill the expectation - let c = 17; - } - - #[expect(unused_variables, unused_mut)] - pub fn check_lint_2() { - // unused_mut should fulfill the expectation - let mut c = 17; - let _ = c; - } -} - mod check_fulfilled_expect_in_macro { macro_rules! expect_inside_macro { () => { @@ -54,8 +39,5 @@ fn main() { check_lint_group(); check_multiple_lint_emissions(); - check_multiple_expected_lints::check_lint_1(); - check_multiple_expected_lints::check_lint_2(); - check_fulfilled_expect_in_macro::check_macro(); } diff --git a/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr index 79657eb539395..df7d6584f99dd 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/multiple_expect_attrs.stderr @@ -1,8 +1,8 @@ warning: this lint expectation is unfulfilled - --> $DIR/multiple_expect_attrs.rs:7:1 + --> $DIR/multiple_expect_attrs.rs:7:10 | LL | #[expect(unused_variables)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^^^^^ | = note: `#[warn(unfulfilled_lint_expectations)]` on by default From 43dc430f5275a520dab25a5bc1c8a0d0ad2cb6b8 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 25 Nov 2021 10:45:25 +0100 Subject: [PATCH 08/13] Test `expect` with `forbid` and fix doc errors (RFC-2383) * Add test to expect and the forbid a lint (RFC 2383) --- compiler/rustc_lint_defs/src/lib.rs | 12 ++--- .../expect_nested_lint_levels.rs | 12 ++++- .../expect_nested_lint_levels.stderr | 28 ++++++++-- .../expect_with_forbid.rs | 34 +++++++++++++ .../expect_with_forbid.stderr | 51 +++++++++++++++++++ 5 files changed, 124 insertions(+), 13 deletions(-) create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.rs create mode 100644 src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.stderr diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index e350eeea6c850..66629195153b4 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -50,10 +50,9 @@ pub enum Applicability { Unspecified, } -/// Each lint expectation has a `LintExpectationId` assigned by the -/// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`]. Expected -/// [`Diagnostic`][`rustc_errors::Diagnostic`]s get the lint level `Expect` which -/// stores the `LintExpectationId` to match it with the actual expectation later on. +/// Each lint expectation has a `LintExpectationId` assigned by the `LintLevelsBuilder`. +/// Expected `Diagnostic`s get the lint level `Expect` which stores the `LintExpectationId` +/// to match it with the actual expectation later on. /// /// The `LintExpectationId` has to be has stable between compilations, as diagnostic /// instances might be loaded from cache. Lint messages can be emitted during an @@ -61,8 +60,7 @@ pub enum Applicability { /// HIR tree. The AST doesn't have enough information to create a stable id. The /// `LintExpectationId` will instead store the [`AttrId`] defining the expectation. /// These `LintExpectationId` will be updated to use the stable [`HirId`] once the -/// AST has been lowered. The transformation is done by the -/// [`LintLevelsBuilder`][`rustc_lint::levels::LintLevelsBuilder`] +/// AST has been lowered. The transformation is done by the `LintLevelsBuilder` /// /// Each lint inside the `expect` attribute is tracked individually, the `lint_index` /// identifies the lint inside the attribute and ensures that the IDs are unique. @@ -135,7 +133,7 @@ impl ToStableHashKey for LintExpectation /// Setting for how to handle a lint. /// -/// See: https://doc.rust-lang.org/rustc/lints/levels.html +/// See: #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)] pub enum Level { /// The `allow` level will not issue any message. diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs index 9c3d1fe6f0f30..cad6b836c7a72 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs @@ -1,4 +1,3 @@ -// check-pass // ignore-tidy-linelength #![feature(lint_reasons)] @@ -37,7 +36,18 @@ mod oof { let mut v = 0; //~^ WARNING variable does not need to be mutable [unused_mut] //~| NOTE this overrides the previous `expect` lint level and warns about the `unused_mut` lint here + //~| HELP remove this `mut` } } +#[expect(unused_variables)] +//~^ WARNING this lint expectation is unfulfilled +#[forbid(unused_variables)] +//~^ NOTE the lint level is defined here +fn check_expect_then_forbid() { + let this_is_my_function = 3; + //~^ ERROR unused variable: `this_is_my_function` [unused_variables] + //~| HELP if this is intentional, prefix it with an underscore +} + fn main() {} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr index b3a227f0d3d8e..353cbc341f24a 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr @@ -1,5 +1,17 @@ +error: unused variable: `this_is_my_function` + --> $DIR/expect_nested_lint_levels.rs:48:9 + | +LL | let this_is_my_function = 3; + | ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, prefix it with an underscore: `_this_is_my_function` + | +note: the lint level is defined here + --> $DIR/expect_nested_lint_levels.rs:45:10 + | +LL | #[forbid(unused_variables)] + | ^^^^^^^^^^^^^^^^ + warning: variable does not need to be mutable - --> $DIR/expect_nested_lint_levels.rs:37:13 + --> $DIR/expect_nested_lint_levels.rs:36:13 | LL | let mut v = 0; | ----^ @@ -8,13 +20,13 @@ LL | let mut v = 0; | = note: this overrides the previous `expect` lint level and warns about the `unused_mut` lint here note: the lint level is defined here - --> $DIR/expect_nested_lint_levels.rs:32:9 + --> $DIR/expect_nested_lint_levels.rs:31:9 | LL | unused_mut, | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:24:5 + --> $DIR/expect_nested_lint_levels.rs:23:5 | LL | unused_mut, | ^^^^^^^^^^ @@ -23,12 +35,18 @@ LL | unused_mut, = note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:8:5 + --> $DIR/expect_nested_lint_levels.rs:7:5 | LL | unused_mut, | ^^^^^^^^^^ | = note: this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered -warning: 3 warnings emitted +warning: this lint expectation is unfulfilled + --> $DIR/expect_nested_lint_levels.rs:43:10 + | +LL | #[expect(unused_variables)] + | ^^^^^^^^^^^^^^^^ + +error: aborting due to previous error; 4 warnings emitted diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.rs new file mode 100644 index 0000000000000..479ee198e1743 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.rs @@ -0,0 +1,34 @@ +#![feature(lint_reasons)] + +#[forbid(unused_variables)] +//~^ NOTE `forbid` level set here +//~| NOTE `forbid` level set here +#[expect(unused_variables)] +//~^ ERROR incompatible with previous forbid [E0453] +//~| NOTE overruled by previous forbid +//~| ERROR incompatible with previous forbid [E0453] +//~| NOTE overruled by previous forbid +fn expect_forbidden_lint_1() {} + +#[forbid(while_true)] +//~^ NOTE `forbid` level set here +//~| NOTE `forbid` level set here +//~| NOTE the lint level is defined here +#[expect(while_true)] +//~^ ERROR incompatible with previous forbid [E0453] +//~| NOTE overruled by previous forbid +//~| ERROR incompatible with previous forbid [E0453] +//~| NOTE overruled by previous forbid +fn expect_forbidden_lint_2() { + // This while loop will produce a `while_true` lint as the lint level + // at this node is still `forbid` and the `while_true` check happens + // before the compilation terminates due to `E0453` + while true {} + //~^ ERROR denote infinite loops with `loop { ... }` + //~| HELP use `loop` +} + +fn main() { + expect_forbidden_lint_1(); + expect_forbidden_lint_2(); +} diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.stderr new file mode 100644 index 0000000000000..a8116e93404f2 --- /dev/null +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_with_forbid.stderr @@ -0,0 +1,51 @@ +error[E0453]: expect(unused_variables) incompatible with previous forbid + --> $DIR/expect_with_forbid.rs:6:10 + | +LL | #[forbid(unused_variables)] + | ---------------- `forbid` level set here +... +LL | #[expect(unused_variables)] + | ^^^^^^^^^^^^^^^^ overruled by previous forbid + +error[E0453]: expect(while_true) incompatible with previous forbid + --> $DIR/expect_with_forbid.rs:17:10 + | +LL | #[forbid(while_true)] + | ---------- `forbid` level set here +... +LL | #[expect(while_true)] + | ^^^^^^^^^^ overruled by previous forbid + +error[E0453]: expect(unused_variables) incompatible with previous forbid + --> $DIR/expect_with_forbid.rs:6:10 + | +LL | #[forbid(unused_variables)] + | ---------------- `forbid` level set here +... +LL | #[expect(unused_variables)] + | ^^^^^^^^^^^^^^^^ overruled by previous forbid + +error[E0453]: expect(while_true) incompatible with previous forbid + --> $DIR/expect_with_forbid.rs:17:10 + | +LL | #[forbid(while_true)] + | ---------- `forbid` level set here +... +LL | #[expect(while_true)] + | ^^^^^^^^^^ overruled by previous forbid + +error: denote infinite loops with `loop { ... }` + --> $DIR/expect_with_forbid.rs:26:5 + | +LL | while true {} + | ^^^^^^^^^^ help: use `loop` + | +note: the lint level is defined here + --> $DIR/expect_with_forbid.rs:13:10 + | +LL | #[forbid(while_true)] + | ^^^^^^^^^^ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0453`. From a14456f91f2385cbf1a0f3e2108f9684ba56f281 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 24 Nov 2021 23:21:10 +0100 Subject: [PATCH 09/13] Reduced the size of `LintExpectationId` by 12 bytes (RFC-2383) --- compiler/rustc_lint/src/levels.rs | 5 +++-- compiler/rustc_lint_defs/src/lib.rs | 14 +++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 01454d2fbac00..7045f8cf06ceb 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -312,7 +312,7 @@ impl<'s> LintLevelsBuilder<'s> { for (lint_index, li) in metas.iter_mut().enumerate() { let level = match level { Level::Expect(mut id) => { - id.set_lint_index(Some(lint_index)); + id.set_lint_index(Some(lint_index as u16)); Level::Expect(id) } level => level, @@ -601,7 +601,8 @@ impl<'s> LintLevelsBuilder<'s> { hir_id: HirId, attr_index: usize, ) -> LintExpectationId { - let stable_id = LintExpectationId::Stable { hir_id, attr_index, lint_index: None }; + let stable_id = + LintExpectationId::Stable { hir_id, attr_index: attr_index as u16, lint_index: None }; self.expectation_id_map.insert(unstable_id, stable_id); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 66629195153b4..2e5fd479738be 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -64,16 +64,20 @@ pub enum Applicability { /// /// Each lint inside the `expect` attribute is tracked individually, the `lint_index` /// identifies the lint inside the attribute and ensures that the IDs are unique. +/// +/// 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 +/// have that amount of lints listed. `u16` values should therefore suffice. #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash, Encodable, Decodable)] pub enum LintExpectationId { /// Used for lints emitted during the `EarlyLintPass`. This id is not /// has stable and should not be cached. - Unstable { attr_id: AttrId, lint_index: Option }, + Unstable { attr_id: AttrId, lint_index: Option }, /// The [`HirId`] that the lint expectation is attached to. This id is /// stable and can be cached. The additional index ensures that nodes with /// several expectations can correctly match diagnostics to the individual /// expectation. - Stable { hir_id: HirId, attr_index: usize, lint_index: Option }, + Stable { hir_id: HirId, attr_index: u16, lint_index: Option }, } impl LintExpectationId { @@ -84,14 +88,14 @@ impl LintExpectationId { } } - pub fn get_lint_index(&self) -> Option { + 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) { + pub fn set_lint_index(&mut self, new_lint_index: Option) { let (LintExpectationId::Unstable { ref mut lint_index, .. } | LintExpectationId::Stable { ref mut lint_index, .. }) = self; @@ -116,7 +120,7 @@ impl HashStable for LintExpectationId { } impl ToStableHashKey for LintExpectationId { - type KeyType = (HirId, usize, usize); + type KeyType = (HirId, u16, u16); #[inline] fn to_stable_hash_key(&self, _: &HCX) -> Self::KeyType { From 3414ad95517933c40d8e66c14b951ee5ede3e0cb Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 25 Nov 2021 17:45:11 +0100 Subject: [PATCH 10/13] Emit `unfullfilled_lint_expectation` using a `HirId` for performance (RFC-2383) --- compiler/rustc_lint/src/expect.rs | 29 +++++++++------- compiler/rustc_lint/src/levels.rs | 56 ++++++------------------------- compiler/rustc_middle/src/lint.rs | 13 ++----- 3 files changed, 29 insertions(+), 69 deletions(-) diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index db0756f5f54e2..5dcd1f0fbe6be 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,10 +1,9 @@ use crate::builtin; use rustc_data_structures::fx::FxHashMap; -use rustc_middle::lint::struct_lint_level; +use rustc_hir::HirId; use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; use rustc_session::lint::LintExpectationId; use rustc_span::symbol::sym; -use rustc_span::MultiSpan; pub fn check_expectations(tcx: TyCtxt<'_>) { if !tcx.sess.features_untracked().enabled(sym::lint_reasons) { @@ -17,24 +16,28 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { for (id, expectation) in lint_expectations { if !fulfilled_expectations.contains(id) { - emit_unfulfilled_expectation_lint(tcx, expectation); + // This check will always be true, since `lint_expectations` only + // holds stable ids + if let LintExpectationId::Stable { hir_id, .. } = id { + emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation); + } } } } -fn emit_unfulfilled_expectation_lint(tcx: TyCtxt<'_>, expectation: &LintExpectation) { +fn emit_unfulfilled_expectation_lint( + tcx: TyCtxt<'_>, + hir_id: HirId, + expectation: &LintExpectation, +) { // FIXME: The current implementation doesn't cover cases where the // `unfulfilled_lint_expectations` is actually expected by another lint - // expectation. This can be added here as we have the lint level of this - // expectation, and we can also mark the lint expectation it would fulfill - // as such. This is currently not implemented to get some early feedback - // before diving deeper into this. - struct_lint_level( - tcx.sess, + // expectation. This can be added here by checking the lint level and + // retrieving the `LintExpectationId` if it was expected. + tcx.struct_span_lint_hir( builtin::UNFULFILLED_LINT_EXPECTATIONS, - expectation.emission_level, - expectation.emission_level_source, - Some(MultiSpan::from_span(expectation.emission_span)), + hir_id, + expectation.emission_span, |diag| { let mut diag = diag.build("this lint expectation is unfulfilled"); if let Some(rationale) = expectation.reason { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 7045f8cf06ceb..f40abff7fc059 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -245,9 +245,9 @@ impl<'s> LintLevelsBuilder<'s> { for (attr_index, attr) in attrs.iter().enumerate() { let level = match Level::from_attr(attr) { None => continue, - Some(Level::Expect(unstable_id)) if source_hir_id.is_some() => { - let stable_id = - self.create_stable_id(unstable_id, source_hir_id.unwrap(), attr_index); + Some(Level::Expect(unstable_id)) if let Some(hir_id) = source_hir_id => { + let stable_id = self.create_stable_id(unstable_id, hir_id, attr_index); + Level::Expect(stable_id) } Some(lvl) => lvl, @@ -303,12 +303,6 @@ impl<'s> LintLevelsBuilder<'s> { } } - let (unfulfilled_lint_lvl, unfulfilled_lint_src) = self.sets.get_lint_level( - builtin::UNFULFILLED_LINT_EXPECTATIONS, - self.cur, - Some(&specs), - &sess, - ); for (lint_index, li) in metas.iter_mut().enumerate() { let level = match level { Level::Expect(mut id) => { @@ -360,15 +354,8 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(&mut specs, id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations.insert( - expect_id, - LintExpectation::new( - reason, - sp, - unfulfilled_lint_lvl, - unfulfilled_lint_src, - ), - ); + self.lint_expectations + .insert(expect_id, LintExpectation::new(reason, sp)); } } @@ -386,15 +373,8 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(&mut specs, *id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations.insert( - expect_id, - LintExpectation::new( - reason, - sp, - unfulfilled_lint_lvl, - unfulfilled_lint_src, - ), - ); + self.lint_expectations + .insert(expect_id, LintExpectation::new(reason, sp)); } } Err((Some(ids), ref new_lint_name)) => { @@ -433,15 +413,8 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(&mut specs, *id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations.insert( - expect_id, - LintExpectation::new( - reason, - sp, - unfulfilled_lint_lvl, - unfulfilled_lint_src, - ), - ); + self.lint_expectations + .insert(expect_id, LintExpectation::new(reason, sp)); } } Err((None, _)) => { @@ -537,15 +510,8 @@ impl<'s> LintLevelsBuilder<'s> { self.insert_spec(&mut specs, id, (level, src)); } if let Level::Expect(expect_id) = level { - self.lint_expectations.insert( - expect_id, - LintExpectation::new( - reason, - sp, - unfulfilled_lint_lvl, - unfulfilled_lint_src, - ), - ); + self.lint_expectations + .insert(expect_id, LintExpectation::new(reason, sp)); } } else { panic!("renamed lint does not exist: {}", new_name); diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index f680843f9e4e0..689d204b25e90 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -204,20 +204,11 @@ pub struct LintExpectation { pub reason: Option, /// The [`Span`] of the attribute that this expectation originated from. pub emission_span: Span, - /// The [`Level`] that this lint diagnostic should be emitted if unfulfilled. - pub emission_level: Level, - /// The [`LintLevelSource`] information needed for [`struct_lint_level`]. - pub emission_level_source: LintLevelSource, } impl LintExpectation { - pub fn new( - reason: Option, - attr_span: Span, - emission_level: Level, - emission_level_source: LintLevelSource, - ) -> Self { - Self { reason, emission_span: attr_span, emission_level, emission_level_source } + pub fn new(reason: Option, attr_span: Span) -> Self { + Self { reason, emission_span: attr_span } } } From 4887eb7b2d0e427a0dbf07bfade329101389e32a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Thu, 25 Nov 2021 21:59:55 +0100 Subject: [PATCH 11/13] Added `panics` for unreachable states for expectations (RFC 2383) --- compiler/rustc_errors/src/lib.rs | 28 ++++++++++++++++------------ compiler/rustc_lint/src/expect.rs | 2 ++ 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index b53208a6e5d1c..6dc91a5aa9c98 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -942,18 +942,22 @@ impl Handler { let mut inner = self.inner.borrow_mut(); for mut diag in diags.into_iter() { - if let Some(mut unstable_id) = diag.level.get_expectation_id() { - let lint_index = unstable_id.get_lint_index(); - - // The unstable to stable map only maps the unstable it to a stable id - // the lint index is manually transferred here. - unstable_id.set_lint_index(None); - if let Some(mut stable_id) = unstable_to_stable.get(&unstable_id).map(|id| *id) { - stable_id.set_lint_index(lint_index); - diag.level = Level::Expect(stable_id); - inner.fulfilled_expectations.insert(stable_id); - } - } + let mut unstable_id = diag + .level + .get_expectation_id() + .expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`"); + + // The unstable to stable map only maps the unstable it to a stable id + // the lint index is manually transferred here. + let lint_index = unstable_id.get_lint_index(); + unstable_id.set_lint_index(None); + let mut stable_id = *unstable_to_stable + .get(&unstable_id) + .expect("each unstable `LintExpectationId` must have a matching stable id"); + + stable_id.set_lint_index(lint_index); + diag.level = Level::Expect(stable_id); + inner.fulfilled_expectations.insert(stable_id); (*TRACK_DIAGNOSTICS)(&diag); } diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 5dcd1f0fbe6be..7611b41b97eac 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -20,6 +20,8 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { // holds stable ids if let LintExpectationId::Stable { hir_id, .. } = id { emit_unfulfilled_expectation_lint(tcx, *hir_id, expectation); + } else { + unreachable!("at this stage all `LintExpectationId`s are stable"); } } } From defc056ccc8d0a64048b65915c9d9b29fd5d4ba2 Mon Sep 17 00:00:00 2001 From: xFrednet Date: Fri, 18 Feb 2022 12:00:16 +0100 Subject: [PATCH 12/13] Address review comments --- compiler/rustc_errors/src/lib.rs | 8 ++++++-- compiler/rustc_lint_defs/src/lib.rs | 4 ++-- compiler/rustc_middle/src/lint.rs | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 6dc91a5aa9c98..5a9b89d31d12f 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -947,8 +947,8 @@ impl Handler { .get_expectation_id() .expect("all diagnostics inside `unstable_expect_diagnostics` must have a `LintExpectationId`"); - // The unstable to stable map only maps the unstable it to a stable id - // the lint index is manually transferred here. + // The unstable to stable map only maps the unstable `AttrId` to a stable `HirId` with an attribute index. + // The lint index inside the attribute is manually transferred here. let lint_index = unstable_id.get_lint_index(); unstable_id.set_lint_index(None); let mut stable_id = *unstable_to_stable @@ -966,6 +966,10 @@ impl Handler { /// This methods steals all [`LintExpectationId`]s that are stored inside /// [`HandlerInner`] and indicate that the linked expectation has been fulfilled. pub fn steal_fulfilled_expectation_ids(&self) -> FxHashSet { + assert!( + self.inner.borrow().unstable_expect_diagnostics.is_empty(), + "`HandlerInner::unstable_expect_diagnostics` should be empty at this point", + ); std::mem::take(&mut self.inner.borrow_mut().fulfilled_expectations) } } diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 2e5fd479738be..c78428765bb89 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -142,12 +142,12 @@ impl ToStableHashKey for LintExpectation pub enum Level { /// The `allow` level will not issue any message. Allow, - /// The `expect` level will suppress the lint message but intern produce a message + /// The `expect` level will suppress the lint message but in turn produce a message /// if the lint wasn't issued in the expected scope. `Expect` should not be used as /// an initial level for a lint. /// /// Note that this still means that the lint is enabled in this position and should - /// be emitted, this will intern fulfill the expectation and suppress the lint. + /// be emitted, this will in turn fulfill the expectation and suppress the lint. /// /// See RFC 2383. /// diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index 689d204b25e90..cc99e4dfac96d 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -387,7 +387,7 @@ pub fn struct_lint_level<'s, 'd>( // Lint diagnostics that are covered by the expect level will not be emitted outside // the compiler. It is therefore not necessary to add any information for the user. - // This will therefore directly call the decorate function which will intern emit + // This will therefore directly call the decorate function which will in turn emit // the `Diagnostic`. if let Level::Expect(_) = level { let name = lint.name_lower(); From 5275d02433c95c577febf76271c35f7d5f86ed4a Mon Sep 17 00:00:00 2001 From: xFrednet Date: Wed, 2 Mar 2022 18:10:07 +0100 Subject: [PATCH 13/13] Use Vec for expectations to have a constant order (RFC-2383) --- compiler/rustc_lint/src/expect.rs | 4 +-- compiler/rustc_lint/src/levels.rs | 10 +++---- compiler/rustc_middle/src/lint.rs | 2 +- .../expect_multiple_lints.stderr | 28 +++++++++---------- .../expect_nested_lint_levels.rs | 2 +- .../expect_nested_lint_levels.stderr | 8 +++--- 6 files changed, 26 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 7611b41b97eac..e6c9d0b0ab000 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -1,5 +1,4 @@ use crate::builtin; -use rustc_data_structures::fx::FxHashMap; use rustc_hir::HirId; use rustc_middle::{lint::LintExpectation, ty::TyCtxt}; use rustc_session::lint::LintExpectationId; @@ -11,8 +10,7 @@ pub fn check_expectations(tcx: TyCtxt<'_>) { } let fulfilled_expectations = tcx.sess.diagnostic().steal_fulfilled_expectation_ids(); - let lint_expectations: &FxHashMap = - &tcx.lint_levels(()).lint_expectations; + let lint_expectations = &tcx.lint_levels(()).lint_expectations; for (id, expectation) in lint_expectations { if !fulfilled_expectations.contains(id) { diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f40abff7fc059..f46f74fa45fb0 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -45,7 +45,7 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap { pub struct LintLevelsBuilder<'s> { sess: &'s Session, - lint_expectations: FxHashMap, + lint_expectations: Vec<(LintExpectationId, LintExpectation)>, /// Each expectation has a stable and an unstable identifier. This map /// is used to map from unstable to stable [`LintExpectationId`]s. expectation_id_map: FxHashMap, @@ -355,7 +355,7 @@ impl<'s> LintLevelsBuilder<'s> { } if let Level::Expect(expect_id) = level { self.lint_expectations - .insert(expect_id, LintExpectation::new(reason, sp)); + .push((expect_id, LintExpectation::new(reason, sp))); } } @@ -374,7 +374,7 @@ impl<'s> LintLevelsBuilder<'s> { } if let Level::Expect(expect_id) = level { self.lint_expectations - .insert(expect_id, LintExpectation::new(reason, sp)); + .push((expect_id, LintExpectation::new(reason, sp))); } } Err((Some(ids), ref new_lint_name)) => { @@ -414,7 +414,7 @@ impl<'s> LintLevelsBuilder<'s> { } if let Level::Expect(expect_id) = level { self.lint_expectations - .insert(expect_id, LintExpectation::new(reason, sp)); + .push((expect_id, LintExpectation::new(reason, sp))); } } Err((None, _)) => { @@ -511,7 +511,7 @@ impl<'s> LintLevelsBuilder<'s> { } if let Level::Expect(expect_id) = level { self.lint_expectations - .insert(expect_id, LintExpectation::new(reason, sp)); + .push((expect_id, LintExpectation::new(reason, sp))); } } else { panic!("renamed lint does not exist: {}", new_name); diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index cc99e4dfac96d..894947fa70d96 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -159,7 +159,7 @@ pub struct LintLevelMap { /// /// The [`LintExpectationId`] is stored as a part of the [`Expect`](Level::Expect) /// lint level. - pub lint_expectations: FxHashMap, + pub lint_expectations: Vec<(LintExpectationId, LintExpectation)>, pub sets: LintLevelSets, pub id_to_set: FxHashMap, } diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr index 25299aa7ec2fc..90ee744b26b01 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_multiple_lints.stderr @@ -7,10 +7,10 @@ LL | #[expect(unused_variables, unused_mut, while_true)] = note: `#[warn(unfulfilled_lint_expectations)]` on by default warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:28:10 + --> $DIR/expect_multiple_lints.rs:10:40 | LL | #[expect(unused_variables, unused_mut, while_true)] - | ^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^ warning: this lint expectation is unfulfilled --> $DIR/expect_multiple_lints.rs:19:10 @@ -19,34 +19,34 @@ LL | #[expect(unused_variables, unused_mut, while_true)] | ^^^^^^^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:28:28 + --> $DIR/expect_multiple_lints.rs:19:40 | LL | #[expect(unused_variables, unused_mut, while_true)] - | ^^^^^^^^^^ + | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:36:18 + --> $DIR/expect_multiple_lints.rs:28:10 | -LL | #[expect(unused, while_true)] - | ^^^^^^^^^^ +LL | #[expect(unused_variables, unused_mut, while_true)] + | ^^^^^^^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:19:40 + --> $DIR/expect_multiple_lints.rs:28:28 | LL | #[expect(unused_variables, unused_mut, while_true)] - | ^^^^^^^^^^ + | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:45:10 + --> $DIR/expect_multiple_lints.rs:36:18 | LL | #[expect(unused, while_true)] - | ^^^^^^ + | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_multiple_lints.rs:10:40 + --> $DIR/expect_multiple_lints.rs:45:10 | -LL | #[expect(unused_variables, unused_mut, while_true)] - | ^^^^^^^^^^ +LL | #[expect(unused, while_true)] + | ^^^^^^ warning: 8 warnings emitted diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs index cad6b836c7a72..8f94bd6ec6cb8 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.rs @@ -6,6 +6,7 @@ #[expect( unused_mut, //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] + //~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default //~| NOTE this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered reason = "this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered" )] @@ -22,7 +23,6 @@ mod foo { #[expect( unused_mut, //~^ WARNING this lint expectation is unfulfilled [unfulfilled_lint_expectations] - //~| NOTE `#[warn(unfulfilled_lint_expectations)]` on by default //~| NOTE this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered reason = "this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered" )] diff --git a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr index 353cbc341f24a..370e51bf70a7a 100644 --- a/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr +++ b/src/test/ui/lint/rfc-2383-lint-reason/expect_nested_lint_levels.stderr @@ -26,21 +26,21 @@ LL | unused_mut, | ^^^^^^^^^^ warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:23:5 + --> $DIR/expect_nested_lint_levels.rs:7:5 | LL | unused_mut, | ^^^^^^^^^^ | = note: `#[warn(unfulfilled_lint_expectations)]` on by default - = note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered + = note: this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered warning: this lint expectation is unfulfilled - --> $DIR/expect_nested_lint_levels.rs:7:5 + --> $DIR/expect_nested_lint_levels.rs:24:5 | LL | unused_mut, | ^^^^^^^^^^ | - = note: this `expect` is overridden by a `allow` attribute before the `unused_mut` lint is triggered + = note: this `expect` is overridden by a `warn` attribute before the `unused_mut` lint is triggered warning: this lint expectation is unfulfilled --> $DIR/expect_nested_lint_levels.rs:43:10