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)
This commit is contained in:
parent
f467a58b7b
commit
2ca9037b61
7 changed files with 102 additions and 19 deletions
|
@ -133,11 +133,7 @@ impl Diagnostic {
|
||||||
| Level::Error { .. }
|
| Level::Error { .. }
|
||||||
| Level::FailureNote => true,
|
| Level::FailureNote => true,
|
||||||
|
|
||||||
Level::Warning
|
Level::Warning | Level::Note | Level::Help | Level::Allow | Level::Expect(_) => false,
|
||||||
| Level::Note
|
|
||||||
| Level::Help
|
|
||||||
| Level::Allow
|
|
||||||
| Level::Expect(_) => false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -451,6 +451,15 @@ struct HandlerInner {
|
||||||
deduplicated_warn_count: usize,
|
deduplicated_warn_count: usize,
|
||||||
|
|
||||||
future_breakage_diagnostics: Vec<Diagnostic>,
|
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.
|
/// A key denoting where from a diagnostic was stashed.
|
||||||
|
@ -571,6 +580,7 @@ impl Handler {
|
||||||
emitted_diagnostics: Default::default(),
|
emitted_diagnostics: Default::default(),
|
||||||
stashed_diagnostics: Default::default(),
|
stashed_diagnostics: Default::default(),
|
||||||
future_breakage_diagnostics: Vec::new(),
|
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]) {
|
pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
|
||||||
self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
|
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 {
|
impl HandlerInner {
|
||||||
|
@ -959,7 +975,8 @@ impl HandlerInner {
|
||||||
|
|
||||||
(*TRACK_DIAGNOSTICS)(diagnostic);
|
(*TRACK_DIAGNOSTICS)(diagnostic);
|
||||||
|
|
||||||
if let Level::Expect(_) = diagnostic.level {
|
if let Level::Expect(expectation_id) = diagnostic.level {
|
||||||
|
self.fulfilled_expectations.insert(expectation_id);
|
||||||
return;
|
return;
|
||||||
} else if diagnostic.level == Allow {
|
} else if diagnostic.level == Allow {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,17 +7,15 @@ use rustc_errors::{struct_span_err, Applicability, Diagnostic};
|
||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::{intravisit, HirId};
|
use rustc_hir::{intravisit, HirId};
|
||||||
use rustc_middle::hir::nested_filter;
|
use rustc_middle::hir::nested_filter;
|
||||||
use rustc_middle::lint::LevelAndSource;
|
|
||||||
use rustc_middle::lint::LintDiagnosticBuilder;
|
|
||||||
use rustc_middle::lint::{
|
use rustc_middle::lint::{
|
||||||
struct_lint_level, LintLevelMap, LintLevelSets, LintLevelSource, LintSet, LintStackIndex,
|
struct_lint_level, LevelAndSource, LintDiagnosticBuilder, LintExpectation, LintLevelMap,
|
||||||
COMMAND_LINE,
|
LintLevelSets, LintLevelSource, LintSet, LintStackIndex, COMMAND_LINE,
|
||||||
};
|
};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{RegisteredTools, TyCtxt};
|
use rustc_middle::ty::{RegisteredTools, TyCtxt};
|
||||||
use rustc_session::lint::{
|
use rustc_session::lint::{
|
||||||
builtin::{self, FORBIDDEN_LINT_GROUPS},
|
builtin::{self, FORBIDDEN_LINT_GROUPS},
|
||||||
Level, Lint, LintId,
|
Level, Lint, LintExpectationId, LintId,
|
||||||
};
|
};
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
@ -44,6 +42,7 @@ fn lint_levels(tcx: TyCtxt<'_>, (): ()) -> LintLevelMap {
|
||||||
|
|
||||||
pub struct LintLevelsBuilder<'s> {
|
pub struct LintLevelsBuilder<'s> {
|
||||||
sess: &'s Session,
|
sess: &'s Session,
|
||||||
|
lint_expectations: FxHashMap<LintExpectationId, LintExpectation>,
|
||||||
sets: LintLevelSets,
|
sets: LintLevelSets,
|
||||||
id_to_set: FxHashMap<HirId, LintStackIndex>,
|
id_to_set: FxHashMap<HirId, LintStackIndex>,
|
||||||
cur: LintStackIndex,
|
cur: LintStackIndex,
|
||||||
|
@ -66,6 +65,7 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut builder = LintLevelsBuilder {
|
let mut builder = LintLevelsBuilder {
|
||||||
sess,
|
sess,
|
||||||
|
lint_expectations: Default::default(),
|
||||||
sets: LintLevelSets::new(),
|
sets: LintLevelSets::new(),
|
||||||
cur: COMMAND_LINE,
|
cur: COMMAND_LINE,
|
||||||
id_to_set: Default::default(),
|
id_to_set: Default::default(),
|
||||||
|
@ -231,7 +231,7 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
let sess = self.sess;
|
let sess = self.sess;
|
||||||
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
|
let bad_attr = |span| struct_span_err!(sess, span, E0452, "malformed lint attribute input");
|
||||||
for attr in attrs {
|
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
|
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 {
|
if !is_crate_node {
|
||||||
|
@ -563,7 +583,11 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_map(self) -> LintLevelMap {
|
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,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,10 +133,10 @@ impl Level {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a symbol to a 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 {
|
match x {
|
||||||
sym::allow => Some(Level::Allow),
|
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::warn => Some(Level::Warn),
|
||||||
sym::deny => Some(Level::Deny),
|
sym::deny => Some(Level::Deny),
|
||||||
sym::forbid => Some(Level::Forbid),
|
sym::forbid => Some(Level::Forbid),
|
||||||
|
|
|
@ -6,6 +6,7 @@ use rustc_errors::{Diagnostic, DiagnosticBuilder, DiagnosticId};
|
||||||
use rustc_hir::HirId;
|
use rustc_hir::HirId;
|
||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
use rustc_query_system::ich::StableHashingContext;
|
use rustc_query_system::ich::StableHashingContext;
|
||||||
|
use rustc_session::lint::LintExpectationId;
|
||||||
use rustc_session::lint::{
|
use rustc_session::lint::{
|
||||||
builtin::{self, FORBIDDEN_LINT_GROUPS},
|
builtin::{self, FORBIDDEN_LINT_GROUPS},
|
||||||
FutureIncompatibilityReason, Level, Lint, LintId,
|
FutureIncompatibilityReason, Level, Lint, LintId,
|
||||||
|
@ -153,6 +154,13 @@ impl LintLevelSets {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct LintLevelMap {
|
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 sets: LintLevelSets,
|
||||||
pub id_to_set: FxHashMap<HirId, LintStackIndex>,
|
pub id_to_set: FxHashMap<HirId, LintStackIndex>,
|
||||||
}
|
}
|
||||||
|
@ -178,14 +186,42 @@ impl LintLevelMap {
|
||||||
impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
|
impl<'a> HashStable<StableHashingContext<'a>> for LintLevelMap {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn hash_stable(&self, hcx: &mut StableHashingContext<'a>, hasher: &mut StableHasher) {
|
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);
|
id_to_set.hash_stable(hcx, hasher);
|
||||||
|
lint_expectations.hash_stable(hcx, hasher);
|
||||||
|
|
||||||
hcx.while_hashing_spans(true, |hcx| sets.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, ()>);
|
pub struct LintDiagnosticBuilder<'a>(DiagnosticBuilder<'a, ()>);
|
||||||
|
|
||||||
impl<'a> LintDiagnosticBuilder<'a> {
|
impl<'a> LintDiagnosticBuilder<'a> {
|
||||||
|
@ -225,7 +261,9 @@ pub fn explain_lint_level_source(
|
||||||
Level::Forbid => "-F",
|
Level::Forbid => "-F",
|
||||||
Level::Allow => "-A",
|
Level::Allow => "-A",
|
||||||
Level::ForceWarn => "--force-warn",
|
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('_', "-");
|
let hyphen_case_lint_name = name.replace('_', "-");
|
||||||
if lint_flag_val.as_str() == name {
|
if lint_flag_val.as_str() == name {
|
||||||
|
|
|
@ -2755,7 +2755,11 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||||
return bound;
|
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;
|
return id;
|
||||||
}
|
}
|
||||||
let next = hir.get_parent_node(id);
|
let next = hir.get_parent_node(id);
|
||||||
|
|
|
@ -331,7 +331,11 @@ impl Session {
|
||||||
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
|
pub fn struct_allow(&self, msg: &str) -> DiagnosticBuilder<'_, ()> {
|
||||||
self.diagnostic().struct_allow(msg)
|
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)
|
self.diagnostic().struct_expect(msg, id)
|
||||||
}
|
}
|
||||||
pub fn struct_span_err<S: Into<MultiSpan>>(
|
pub fn struct_span_err<S: Into<MultiSpan>>(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue