about summary refs log tree commit diff
diff options
context:
space:
mode:
authorxFrednet <xFrednet@gmail.com>2021-08-06 23:28:58 +0200
committerxFrednet <xFrednet@gmail.com>2022-03-02 17:46:07 +0100
commit2ca9037b6154227306d9b40a87d79e691f1c9126 (patch)
treeeb09c4b3292e8d8195719aab9c80d4e9b84da6d2
parentf467a58b7bdc330a5af3962df40ad11fe2f05165 (diff)
downloadrust-2ca9037b6154227306d9b40a87d79e691f1c9126.tar.gz
rust-2ca9037b6154227306d9b40a87d79e691f1c9126.zip
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)
-rw-r--r--compiler/rustc_errors/src/diagnostic.rs6
-rw-r--r--compiler/rustc_errors/src/lib.rs19
-rw-r--r--compiler/rustc_lint/src/levels.rs38
-rw-r--r--compiler/rustc_lint_defs/src/lib.rs4
-rw-r--r--compiler/rustc_middle/src/lint.rs42
-rw-r--r--compiler/rustc_middle/src/ty/context.rs6
-rw-r--r--compiler/rustc_session/src/session.rs6
7 files changed, 102 insertions, 19 deletions
diff --git a/compiler/rustc_errors/src/diagnostic.rs b/compiler/rustc_errors/src/diagnostic.rs
index 802f2560601..a59d91ea789 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 41d2b285997..83e52e002ae 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<Diagnostic>,
+
+    /// 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<LintExpectationId>,
 }
 
 /// 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<LintExpectationId> {
+        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 d7cdb08d817..a876e35c0a8 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<LintExpectationId, LintExpectation>,
     sets: LintLevelSets,
     id_to_set: FxHashMap<HirId, LintStackIndex>,
     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 e6643cfd544..6fca39b2c6b 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<Level> {
+    pub fn from_symbol(x: Symbol, possible_lint_expect_id: u32) -> Option<Level> {
         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 5e7fc33953a..9eb7aca13c0 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<LintExpectationId, LintExpectation>,
     pub sets: LintLevelSets,
     pub id_to_set: FxHashMap<HirId, LintStackIndex>,
 }
@@ -178,14 +186,42 @@ impl LintLevelMap {
 impl<'a> HashStable<StableHashingContext<'a>> 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<Symbol>,
+    /// 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<Symbol>,
+        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 f9435682e53..bd48a9867f9 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 edc98abca29..4a7e7e267a2 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<S: Into<MultiSpan>>(