mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
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
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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),
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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>>(
|
||||
|
Loading…
Reference in New Issue
Block a user