Move lint expectation checking into a separate query (RFC 2383)
This commit is contained in:
parent
7f03681cd9
commit
2c5e85249f
8 changed files with 68 additions and 13 deletions
|
@ -1009,6 +1009,10 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// This check has to be run after all lints are done processing. We don't
|
||||||
|
// define a lint filter, as all lint checks should have finished at this point.
|
||||||
|
sess.time("check_lint_expectations", || tcx.check_expectations(None));
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
use crate::builtin;
|
use crate::builtin;
|
||||||
use rustc_hir::HirId;
|
use rustc_hir::HirId;
|
||||||
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::{lint::LintExpectation, ty::TyCtxt};
|
use rustc_middle::{lint::LintExpectation, ty::TyCtxt};
|
||||||
use rustc_session::lint::LintExpectationId;
|
use rustc_session::lint::LintExpectationId;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
|
use rustc_span::Symbol;
|
||||||
|
|
||||||
pub fn check_expectations(tcx: TyCtxt<'_>) {
|
pub(crate) fn provide(providers: &mut Providers) {
|
||||||
|
*providers = Providers { check_expectations, ..*providers };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option<Symbol>) {
|
||||||
if !tcx.sess.features_untracked().enabled(sym::lint_reasons) {
|
if !tcx.sess.features_untracked().enabled(sym::lint_reasons) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +19,9 @@ pub fn check_expectations(tcx: TyCtxt<'_>) {
|
||||||
let lint_expectations = &tcx.lint_levels(()).lint_expectations;
|
let lint_expectations = &tcx.lint_levels(()).lint_expectations;
|
||||||
|
|
||||||
for (id, expectation) in lint_expectations {
|
for (id, expectation) in lint_expectations {
|
||||||
if !fulfilled_expectations.contains(id) {
|
if !fulfilled_expectations.contains(id)
|
||||||
|
&& tool_filter.map_or(true, |filter| expectation.lint_tool == Some(filter))
|
||||||
|
{
|
||||||
// This check will always be true, since `lint_expectations` only
|
// This check will always be true, since `lint_expectations` only
|
||||||
// holds stable ids
|
// holds stable ids
|
||||||
if let LintExpectationId::Stable { hir_id, .. } = id {
|
if let LintExpectationId::Stable { hir_id, .. } = id {
|
||||||
|
|
|
@ -503,7 +503,4 @@ 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));
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -371,7 +371,12 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
};
|
};
|
||||||
self.lint_expectations.push((
|
self.lint_expectations.push((
|
||||||
expect_id,
|
expect_id,
|
||||||
LintExpectation::new(reason, sp, is_unfulfilled_lint_expectations),
|
LintExpectation::new(
|
||||||
|
reason,
|
||||||
|
sp,
|
||||||
|
is_unfulfilled_lint_expectations,
|
||||||
|
tool_name,
|
||||||
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let src = LintLevelSource::Node(
|
let src = LintLevelSource::Node(
|
||||||
|
@ -400,8 +405,10 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
self.insert_spec(*id, (level, src));
|
self.insert_spec(*id, (level, src));
|
||||||
}
|
}
|
||||||
if let Level::Expect(expect_id) = level {
|
if let Level::Expect(expect_id) = level {
|
||||||
self.lint_expectations
|
self.lint_expectations.push((
|
||||||
.push((expect_id, LintExpectation::new(reason, sp, false)));
|
expect_id,
|
||||||
|
LintExpectation::new(reason, sp, false, tool_name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((Some(ids), ref new_lint_name)) => {
|
Err((Some(ids), ref new_lint_name)) => {
|
||||||
|
@ -444,8 +451,10 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
self.insert_spec(*id, (level, src));
|
self.insert_spec(*id, (level, src));
|
||||||
}
|
}
|
||||||
if let Level::Expect(expect_id) = level {
|
if let Level::Expect(expect_id) = level {
|
||||||
self.lint_expectations
|
self.lint_expectations.push((
|
||||||
.push((expect_id, LintExpectation::new(reason, sp, false)));
|
expect_id,
|
||||||
|
LintExpectation::new(reason, sp, false, tool_name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err((None, _)) => {
|
Err((None, _)) => {
|
||||||
|
@ -550,8 +559,10 @@ impl<'s> LintLevelsBuilder<'s> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Level::Expect(expect_id) = level {
|
if let Level::Expect(expect_id) = level {
|
||||||
self.lint_expectations
|
self.lint_expectations.push((
|
||||||
.push((expect_id, LintExpectation::new(reason, sp, false)));
|
expect_id,
|
||||||
|
LintExpectation::new(reason, sp, false, tool_name),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
panic!("renamed lint does not exist: {}", new_name);
|
panic!("renamed lint does not exist: {}", new_name);
|
||||||
|
|
|
@ -109,6 +109,7 @@ pub use rustc_session::lint::{LintArray, LintPass};
|
||||||
|
|
||||||
pub fn provide(providers: &mut Providers) {
|
pub fn provide(providers: &mut Providers) {
|
||||||
levels::provide(providers);
|
levels::provide(providers);
|
||||||
|
expect::provide(providers);
|
||||||
*providers = Providers { lint_mod, ..*providers };
|
*providers = Providers { lint_mod, ..*providers };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,10 @@ pub struct LintExpectation {
|
||||||
/// adjusted to include an additional note. Therefore, we have to track if
|
/// adjusted to include an additional note. Therefore, we have to track if
|
||||||
/// the expectation is for the lint.
|
/// the expectation is for the lint.
|
||||||
pub is_unfulfilled_lint_expectations: bool,
|
pub is_unfulfilled_lint_expectations: bool,
|
||||||
|
/// This will hold the name of the tool that this lint belongs to. For
|
||||||
|
/// the lint `clippy::some_lint` the tool would be `clippy`, the same
|
||||||
|
/// goes for `rustdoc`. This will be `None` for rustc lints
|
||||||
|
pub lint_tool: Option<Symbol>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LintExpectation {
|
impl LintExpectation {
|
||||||
|
@ -217,8 +221,9 @@ impl LintExpectation {
|
||||||
reason: Option<Symbol>,
|
reason: Option<Symbol>,
|
||||||
emission_span: Span,
|
emission_span: Span,
|
||||||
is_unfulfilled_lint_expectations: bool,
|
is_unfulfilled_lint_expectations: bool,
|
||||||
|
lint_tool: Option<Symbol>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self { reason, emission_span, is_unfulfilled_lint_expectations }
|
Self { reason, emission_span, is_unfulfilled_lint_expectations, lint_tool }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -157,6 +157,25 @@ rustc_queries! {
|
||||||
desc { "running analysis passes on this crate" }
|
desc { "running analysis passes on this crate" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This query checks the fulfillment of collected lint expectations.
|
||||||
|
/// All lint emitting queries have to be done before this is executed
|
||||||
|
/// to ensure that all expectations can be fulfilled.
|
||||||
|
///
|
||||||
|
/// This is an extra query to enable other drivers (like rustdoc) to
|
||||||
|
/// only execute a small subset of the [`analysis`] query, while allowing
|
||||||
|
/// lints to be expected. In rustc, this query will be executed as part of
|
||||||
|
/// the [`analysis`] query and doesn't have to be called a second time.
|
||||||
|
///
|
||||||
|
/// Tools can additionally pass in a tool filter. That will restrict the
|
||||||
|
/// expectations to only trigger for lints starting with the listed tool
|
||||||
|
/// name. This is useful for cases were not all linting code from rustc
|
||||||
|
/// was called. With the default `none` all registered lints will also
|
||||||
|
/// be checked for expectation fulfillment.
|
||||||
|
query check_expectations(key: Option<Symbol>) -> () {
|
||||||
|
eval_always
|
||||||
|
desc { "checking lint expectations (RFC 2383)" }
|
||||||
|
}
|
||||||
|
|
||||||
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
|
/// Maps from the `DefId` of an item (trait/struct/enum/fn) to its
|
||||||
/// associated generics.
|
/// associated generics.
|
||||||
query generics_of(key: DefId) -> ty::Generics {
|
query generics_of(key: DefId) -> ty::Generics {
|
||||||
|
|
|
@ -435,6 +435,16 @@ impl Key for Symbol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Key for Option<Symbol> {
|
||||||
|
#[inline(always)]
|
||||||
|
fn query_crate_is_local(&self) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn default_span(&self, _tcx: TyCtxt<'_>) -> Span {
|
||||||
|
DUMMY_SP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Canonical query goals correspond to abstract trait operations that
|
/// Canonical query goals correspond to abstract trait operations that
|
||||||
/// are not tied to any crate in particular.
|
/// are not tied to any crate in particular.
|
||||||
impl<'tcx, T> Key for Canonical<'tcx, T> {
|
impl<'tcx, T> Key for Canonical<'tcx, T> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue