Do not run lints that cannot emit
Before this change, adding a lint was a difficult matter because it always had some overhead involved. This was because all lints would run, no matter their default level, or if the user had #![allow]ed them. This PR changes that
This commit is contained in:
parent
c926476d01
commit
b4da058595
45 changed files with 264 additions and 50 deletions
|
@ -74,6 +74,12 @@ use crate::{
|
|||
fluent_generated as fluent,
|
||||
};
|
||||
|
||||
use std::default::Default;
|
||||
use std::fmt::Write;
|
||||
|
||||
// hardwired lints from rustc_lint_defs
|
||||
pub use rustc_session::lint::builtin::*;
|
||||
|
||||
declare_lint! {
|
||||
/// The `while_true` lint detects `while true { }`.
|
||||
///
|
||||
|
@ -241,7 +247,8 @@ declare_lint! {
|
|||
/// behavior.
|
||||
UNSAFE_CODE,
|
||||
Allow,
|
||||
"usage of `unsafe` code and other potentially unsound constructs"
|
||||
"usage of `unsafe` code and other potentially unsound constructs",
|
||||
[loadbearing: true]
|
||||
}
|
||||
|
||||
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
|
||||
|
@ -389,6 +396,7 @@ declare_lint! {
|
|||
report_in_external_macro
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct MissingDoc;
|
||||
|
||||
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
|
||||
|
@ -819,8 +827,8 @@ pub struct DeprecatedAttr {
|
|||
|
||||
impl_lint_pass!(DeprecatedAttr => []);
|
||||
|
||||
impl DeprecatedAttr {
|
||||
pub fn new() -> DeprecatedAttr {
|
||||
impl Default for DeprecatedAttr {
|
||||
fn default() -> Self {
|
||||
DeprecatedAttr { depr_attrs: deprecated_attributes() }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -312,6 +312,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
|
|||
fn name(&self) -> &'static str {
|
||||
panic!()
|
||||
}
|
||||
fn get_lints(&self) -> crate::LintVec {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_early_lint_pass {
|
||||
|
|
|
@ -429,7 +429,8 @@ declare_tool_lint! {
|
|||
pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
|
||||
Deny,
|
||||
"prevent creation of diagnostics which cannot be translated",
|
||||
report_in_external_macro: true
|
||||
report_in_external_macro: true,
|
||||
[loadbearing: true]
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
|
@ -442,7 +443,8 @@ declare_tool_lint! {
|
|||
pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
|
||||
Deny,
|
||||
"prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls",
|
||||
report_in_external_macro: true
|
||||
report_in_external_macro: true,
|
||||
[loadbearing: true]
|
||||
}
|
||||
|
||||
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);
|
||||
|
|
|
@ -326,6 +326,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
|
|||
fn name(&self) -> &'static str {
|
||||
panic!()
|
||||
}
|
||||
fn get_lints(&self) -> crate::LintVec {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_late_lint_pass {
|
||||
|
@ -361,13 +364,38 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
|
|||
// Note: `passes` is often empty. In that case, it's faster to run
|
||||
// `builtin_lints` directly rather than bundling it up into the
|
||||
// `RuntimeCombinedLateLintPass`.
|
||||
let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes;
|
||||
if late_module_passes.is_empty() {
|
||||
let store = unerased_lint_store(tcx.sess);
|
||||
|
||||
if store.late_module_passes.is_empty() {
|
||||
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
|
||||
} else {
|
||||
let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
|
||||
passes.push(Box::new(builtin_lints));
|
||||
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
|
||||
let passes: Vec<_> =
|
||||
store.late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
|
||||
|
||||
// Filter unused lints
|
||||
let (lints_to_emit, lints_allowed) = &**tcx.lints_that_can_emit(());
|
||||
// let lints_to_emit = &lints_that_can_emit.0;
|
||||
// let lints_allowed = &lints_that_can_emit.1;
|
||||
|
||||
// Now, we'll filtered passes in a way that discards any lint that won't trigger.
|
||||
// If any lint is a given pass is detected to be emitted, we will keep that pass.
|
||||
// Otherwise, we don't
|
||||
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
|
||||
.into_iter()
|
||||
.filter(|pass| {
|
||||
let pass = LintPass::get_lints(pass);
|
||||
pass.iter().any(|&lint| {
|
||||
let lint_name = name_without_tool(&lint.name.to_lowercase()).to_string();
|
||||
lints_to_emit.contains(&lint_name)
|
||||
|| (!lints_allowed.contains(&lint_name)
|
||||
&& lint.default_level != crate::Level::Allow)
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
|
||||
filtered_passes.push(Box::new(builtin_lints));
|
||||
|
||||
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
|
||||
late_lint_mod_inner(tcx, module_def_id, context, pass);
|
||||
}
|
||||
}
|
||||
|
@ -454,3 +482,10 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) {
|
|||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// Format name ignoring the name, useful for filtering non-used lints.
|
||||
/// For example, 'clippy::my_lint' will turn into 'my_lint'
|
||||
pub(crate) fn name_without_tool(name: &str) -> &str {
|
||||
// Doing some calculations here to account for those separators
|
||||
name.rsplit("::").next().unwrap_or(name)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
|
||||
use rustc_data_structures::{fx::FxIndexMap, sync::Lrc};
|
||||
use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan};
|
||||
use rustc_feature::{Features, GateIssue};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
|
@ -31,7 +31,7 @@ use crate::errors::{
|
|||
OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::late::unerased_lint_store;
|
||||
use crate::late::{unerased_lint_store, name_without_tool};
|
||||
use crate::lints::{
|
||||
DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
|
||||
OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
|
||||
|
@ -115,6 +115,36 @@ impl LintLevelSets {
|
|||
}
|
||||
}
|
||||
|
||||
/// Walk the whole crate collecting nodes where lint levels change
|
||||
/// (e.g. `#[allow]` attributes), and joins that list with the warn-by-default
|
||||
/// (and not allowed in the crate) and CLI lints. The returned value is a tuple
|
||||
/// of 1. The lints that will emit (or at least, should run), and 2.
|
||||
/// The lints that are allowed at the crate level and will not emit.
|
||||
pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(Vec<String>, Vec<String>)> {
|
||||
let mut visitor = LintLevelMinimum::new(tcx);
|
||||
visitor.process_opts();
|
||||
tcx.hir().walk_attributes(&mut visitor);
|
||||
|
||||
let store = unerased_lint_store(&tcx.sess);
|
||||
|
||||
let lint_groups = store.get_lint_groups();
|
||||
for group in lint_groups {
|
||||
let binding = group.0.to_lowercase();
|
||||
let group_name = name_without_tool(&binding).to_string();
|
||||
if visitor.lints_to_emit.contains(&group_name) {
|
||||
for lint in group.1 {
|
||||
visitor.lints_to_emit.push(name_without_tool(&lint.to_string()).to_string());
|
||||
}
|
||||
} else if visitor.lints_allowed.contains(&group_name) {
|
||||
for lint in &group.1 {
|
||||
visitor.lints_allowed.push(name_without_tool(&lint.to_string()).to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Lrc::new((visitor.lints_to_emit, visitor.lints_allowed))
|
||||
}
|
||||
|
||||
#[instrument(level = "trace", skip(tcx), ret)]
|
||||
fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
|
||||
let store = unerased_lint_store(tcx.sess);
|
||||
|
@ -301,6 +331,88 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Visitor with the only function of visiting every item-like in a crate and
|
||||
/// computing the highest level that every lint gets put to.
|
||||
///
|
||||
/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
|
||||
/// uses #[warn(lint)], this visitor will set that lint level as `Warn`
|
||||
struct LintLevelMinimum<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
/// The actual list of detected lints.
|
||||
lints_to_emit: Vec<String>,
|
||||
lints_allowed: Vec<String>,
|
||||
}
|
||||
|
||||
impl<'tcx> LintLevelMinimum<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
|
||||
Self {
|
||||
tcx,
|
||||
// That magic number is the current number of lints + some more for possible future lints
|
||||
lints_to_emit: Vec::with_capacity(230),
|
||||
lints_allowed: Vec::with_capacity(100),
|
||||
}
|
||||
}
|
||||
|
||||
fn process_opts(&mut self) {
|
||||
for (lint, level) in &self.tcx.sess.opts.lint_opts {
|
||||
if *level == Level::Allow {
|
||||
self.lints_allowed.push(lint.clone());
|
||||
} else {
|
||||
self.lints_to_emit.push(lint.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> {
|
||||
type NestedFilter = nested_filter::All;
|
||||
|
||||
fn nested_visit_map(&mut self) -> Self::Map {
|
||||
self.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) {
|
||||
if let Some(meta) = attribute.meta() {
|
||||
if [sym::warn, sym::deny, sym::forbid, sym::expect]
|
||||
.iter()
|
||||
.any(|kind| meta.has_name(*kind))
|
||||
{
|
||||
// SAFETY: Lint attributes are always a metalist inside a
|
||||
// metalist (even with just one lint).
|
||||
for meta_list in meta.meta_item_list().unwrap() {
|
||||
// If it's a tool lint (e.g. clippy::my_clippy_lint)
|
||||
if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list {
|
||||
if meta_item.path.segments.len() == 1 {
|
||||
self.lints_to_emit.push(
|
||||
// SAFETY: Lint attributes can only have literals
|
||||
meta_list.ident().unwrap().name.as_str().to_string(),
|
||||
);
|
||||
} else {
|
||||
self.lints_to_emit
|
||||
.push(meta_item.path.segments[1].ident.name.as_str().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
// We handle #![allow]s differently, as these remove checking rather than adding.
|
||||
} else if meta.has_name(sym::allow)
|
||||
&& let ast::AttrStyle::Inner = attribute.style
|
||||
{
|
||||
for meta_list in meta.meta_item_list().unwrap() {
|
||||
// If it's a tool lint (e.g. clippy::my_clippy_lint)
|
||||
if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list {
|
||||
if meta_item.path.segments.len() == 1 {
|
||||
self.lints_allowed.push(meta_list.name_or_empty().as_str().to_string())
|
||||
} else {
|
||||
self.lints_allowed
|
||||
.push(meta_item.path.segments[1].ident.name.as_str().to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct LintLevelsBuilder<'s, P> {
|
||||
sess: &'s Session,
|
||||
features: &'s Features,
|
||||
|
@ -931,7 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
|
|||
}
|
||||
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
*providers = Providers { shallow_lint_levels_on, ..*providers };
|
||||
*providers =
|
||||
Providers { shallow_lint_levels_on, lints_that_can_emit, ..*providers };
|
||||
}
|
||||
|
||||
pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {
|
||||
|
|
|
@ -170,7 +170,7 @@ early_lint_methods!(
|
|||
[
|
||||
pub BuiltinCombinedEarlyLintPass,
|
||||
[
|
||||
UnusedParens: UnusedParens::new(),
|
||||
UnusedParens: UnusedParens::default(),
|
||||
UnusedBraces: UnusedBraces,
|
||||
UnusedImportBraces: UnusedImportBraces,
|
||||
UnsafeCode: UnsafeCode,
|
||||
|
@ -178,7 +178,7 @@ early_lint_methods!(
|
|||
AnonymousParameters: AnonymousParameters,
|
||||
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
|
||||
NonCamelCaseTypes: NonCamelCaseTypes,
|
||||
DeprecatedAttr: DeprecatedAttr::new(),
|
||||
DeprecatedAttr: DeprecatedAttr::default(),
|
||||
WhileTrue: WhileTrue,
|
||||
NonAsciiIdents: NonAsciiIdents,
|
||||
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
|
||||
|
@ -601,25 +601,25 @@ fn register_builtins(store: &mut LintStore) {
|
|||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
store.register_lints(&LintPassImpl::get_lints());
|
||||
store.register_lints(&LintPassImpl::default().get_lints());
|
||||
store.register_early_pass(|| Box::new(LintPassImpl));
|
||||
store.register_lints(&DefaultHashTypes::get_lints());
|
||||
store.register_lints(&DefaultHashTypes::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
|
||||
store.register_lints(&QueryStability::get_lints());
|
||||
store.register_lints(&QueryStability::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(QueryStability));
|
||||
store.register_lints(&ExistingDocKeyword::get_lints());
|
||||
store.register_lints(&ExistingDocKeyword::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
|
||||
store.register_lints(&TyTyKind::get_lints());
|
||||
store.register_lints(&TyTyKind::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(TyTyKind));
|
||||
store.register_lints(&TypeIr::get_lints());
|
||||
store.register_lints(&TypeIr::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(TypeIr));
|
||||
store.register_lints(&Diagnostics::get_lints());
|
||||
store.register_lints(&Diagnostics::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(Diagnostics));
|
||||
store.register_lints(&BadOptAccess::get_lints());
|
||||
store.register_lints(&BadOptAccess::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
|
||||
store.register_lints(&PassByValue::get_lints());
|
||||
store.register_lints(&PassByValue::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(PassByValue));
|
||||
store.register_lints(&SpanUseEqCtxt::get_lints());
|
||||
store.register_lints(&SpanUseEqCtxt::default().get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt));
|
||||
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
|
||||
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
|
||||
|
|
|
@ -110,7 +110,7 @@ macro_rules! declare_combined_late_lint_pass {
|
|||
|
||||
$v fn get_lints() -> $crate::LintVec {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
$(lints.extend_from_slice(&$pass::default().get_lints());)*
|
||||
lints
|
||||
}
|
||||
}
|
||||
|
@ -124,6 +124,9 @@ macro_rules! declare_combined_late_lint_pass {
|
|||
fn name(&self) -> &'static str {
|
||||
panic!()
|
||||
}
|
||||
fn get_lints(&self) -> LintVec {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -222,7 +225,7 @@ macro_rules! declare_combined_early_lint_pass {
|
|||
|
||||
$v fn get_lints() -> $crate::LintVec {
|
||||
let mut lints = Vec::new();
|
||||
$(lints.extend_from_slice(&$pass::get_lints());)*
|
||||
$(lints.extend_from_slice(&$pass::default().get_lints());)*
|
||||
lints
|
||||
}
|
||||
}
|
||||
|
@ -236,6 +239,9 @@ macro_rules! declare_combined_early_lint_pass {
|
|||
fn name(&self) -> &'static str {
|
||||
panic!()
|
||||
}
|
||||
fn get_lints(&self) -> LintVec {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
@ -165,7 +165,7 @@ declare_lint! {
|
|||
"detects ambiguous wide pointer comparisons"
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[derive(Copy, Clone, Default)]
|
||||
pub(crate) struct TypeLimits {
|
||||
/// Id of the last visited negated expression
|
||||
negated_expr_id: Option<hir::HirId>,
|
||||
|
|
|
@ -1025,8 +1025,8 @@ pub(crate) struct UnusedParens {
|
|||
parens_in_cast_in_lt: Vec<ast::NodeId>,
|
||||
}
|
||||
|
||||
impl UnusedParens {
|
||||
pub(crate) fn new() -> Self {
|
||||
impl Default for UnusedParens {
|
||||
fpub(crate) fn default() -> Self {
|
||||
Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue