Skip to content

Commit b4da058

Browse files
committed
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
1 parent c926476 commit b4da058

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+264
-50
lines changed

Cargo.lock

+1
Original file line numberDiff line numberDiff line change
@@ -4029,6 +4029,7 @@ dependencies = [
40294029
"rustc_hir",
40304030
"rustc_hir_pretty",
40314031
"rustc_index",
4032+
"rustc_lint_defs",
40324033
"rustc_macros",
40334034
"rustc_query_system",
40344035
"rustc_serialize",

compiler/rustc_ast/src/attr/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ impl AttrItem {
223223
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
224224
}
225225

226-
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
226+
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
227227
match &self.args {
228228
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
229229
MetaItemKind::list_from_tokens(args.tokens.clone())

compiler/rustc_lint/src/builtin.rs

+11-3
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ use crate::{
7474
fluent_generated as fluent,
7575
};
7676

77+
use std::default::Default;
78+
use std::fmt::Write;
79+
80+
// hardwired lints from rustc_lint_defs
81+
pub use rustc_session::lint::builtin::*;
82+
7783
declare_lint! {
7884
/// The `while_true` lint detects `while true { }`.
7985
///
@@ -241,7 +247,8 @@ declare_lint! {
241247
/// behavior.
242248
UNSAFE_CODE,
243249
Allow,
244-
"usage of `unsafe` code and other potentially unsound constructs"
250+
"usage of `unsafe` code and other potentially unsound constructs",
251+
[loadbearing: true]
245252
}
246253

247254
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
@@ -389,6 +396,7 @@ declare_lint! {
389396
report_in_external_macro
390397
}
391398

399+
#[derive(Default)]
392400
pub struct MissingDoc;
393401

394402
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
@@ -819,8 +827,8 @@ pub struct DeprecatedAttr {
819827

820828
impl_lint_pass!(DeprecatedAttr => []);
821829

822-
impl DeprecatedAttr {
823-
pub fn new() -> DeprecatedAttr {
830+
impl Default for DeprecatedAttr {
831+
fn default() -> Self {
824832
DeprecatedAttr { depr_attrs: deprecated_attributes() }
825833
}
826834
}

compiler/rustc_lint/src/early.rs

+3
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
312312
fn name(&self) -> &'static str {
313313
panic!()
314314
}
315+
fn get_lints(&self) -> crate::LintVec {
316+
panic!()
317+
}
315318
}
316319

317320
macro_rules! impl_early_lint_pass {

compiler/rustc_lint/src/internal.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -429,7 +429,8 @@ declare_tool_lint! {
429429
pub rustc::UNTRANSLATABLE_DIAGNOSTIC,
430430
Deny,
431431
"prevent creation of diagnostics which cannot be translated",
432-
report_in_external_macro: true
432+
report_in_external_macro: true,
433+
[loadbearing: true]
433434
}
434435

435436
declare_tool_lint! {
@@ -442,7 +443,8 @@ declare_tool_lint! {
442443
pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL,
443444
Deny,
444445
"prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls",
445-
report_in_external_macro: true
446+
report_in_external_macro: true,
447+
[loadbearing: true]
446448
}
447449

448450
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);

compiler/rustc_lint/src/late.rs

+40-5
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
326326
fn name(&self) -> &'static str {
327327
panic!()
328328
}
329+
fn get_lints(&self) -> crate::LintVec {
330+
panic!()
331+
}
329332
}
330333

331334
macro_rules! impl_late_lint_pass {
@@ -361,13 +364,38 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
361364
// Note: `passes` is often empty. In that case, it's faster to run
362365
// `builtin_lints` directly rather than bundling it up into the
363366
// `RuntimeCombinedLateLintPass`.
364-
let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes;
365-
if late_module_passes.is_empty() {
367+
let store = unerased_lint_store(tcx.sess);
368+
369+
if store.late_module_passes.is_empty() {
366370
late_lint_mod_inner(tcx, module_def_id, context, builtin_lints);
367371
} else {
368-
let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
369-
passes.push(Box::new(builtin_lints));
370-
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
372+
let passes: Vec<_> =
373+
store.late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
374+
375+
// Filter unused lints
376+
let (lints_to_emit, lints_allowed) = &**tcx.lints_that_can_emit(());
377+
// let lints_to_emit = &lints_that_can_emit.0;
378+
// let lints_allowed = &lints_that_can_emit.1;
379+
380+
// Now, we'll filtered passes in a way that discards any lint that won't trigger.
381+
// If any lint is a given pass is detected to be emitted, we will keep that pass.
382+
// Otherwise, we don't
383+
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
384+
.into_iter()
385+
.filter(|pass| {
386+
let pass = LintPass::get_lints(pass);
387+
pass.iter().any(|&lint| {
388+
let lint_name = name_without_tool(&lint.name.to_lowercase()).to_string();
389+
lints_to_emit.contains(&lint_name)
390+
|| (!lints_allowed.contains(&lint_name)
391+
&& lint.default_level != crate::Level::Allow)
392+
})
393+
})
394+
.collect();
395+
396+
filtered_passes.push(Box::new(builtin_lints));
397+
398+
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
371399
late_lint_mod_inner(tcx, module_def_id, context, pass);
372400
}
373401
}
@@ -454,3 +482,10 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) {
454482
},
455483
);
456484
}
485+
486+
/// Format name ignoring the name, useful for filtering non-used lints.
487+
/// For example, 'clippy::my_lint' will turn into 'my_lint'
488+
pub(crate) fn name_without_tool(name: &str) -> &str {
489+
// Doing some calculations here to account for those separators
490+
name.rsplit("::").next().unwrap_or(name)
491+
}

compiler/rustc_lint/src/levels.rs

+117-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use rustc_ast_pretty::pprust;
2-
use rustc_data_structures::fx::FxIndexMap;
3-
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
2+
use rustc_data_structures::{fx::FxIndexMap, sync::Lrc};
3+
use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan};
44
use rustc_feature::{Features, GateIssue};
55
use rustc_hir::HirId;
66
use rustc_hir::intravisit::{self, Visitor};
@@ -31,7 +31,7 @@ use crate::errors::{
3131
OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup,
3232
};
3333
use crate::fluent_generated as fluent;
34-
use crate::late::unerased_lint_store;
34+
use crate::late::{unerased_lint_store, name_without_tool};
3535
use crate::lints::{
3636
DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified,
3737
OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint,
@@ -115,6 +115,36 @@ impl LintLevelSets {
115115
}
116116
}
117117

118+
/// Walk the whole crate collecting nodes where lint levels change
119+
/// (e.g. `#[allow]` attributes), and joins that list with the warn-by-default
120+
/// (and not allowed in the crate) and CLI lints. The returned value is a tuple
121+
/// of 1. The lints that will emit (or at least, should run), and 2.
122+
/// The lints that are allowed at the crate level and will not emit.
123+
pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(Vec<String>, Vec<String>)> {
124+
let mut visitor = LintLevelMinimum::new(tcx);
125+
visitor.process_opts();
126+
tcx.hir().walk_attributes(&mut visitor);
127+
128+
let store = unerased_lint_store(&tcx.sess);
129+
130+
let lint_groups = store.get_lint_groups();
131+
for group in lint_groups {
132+
let binding = group.0.to_lowercase();
133+
let group_name = name_without_tool(&binding).to_string();
134+
if visitor.lints_to_emit.contains(&group_name) {
135+
for lint in group.1 {
136+
visitor.lints_to_emit.push(name_without_tool(&lint.to_string()).to_string());
137+
}
138+
} else if visitor.lints_allowed.contains(&group_name) {
139+
for lint in &group.1 {
140+
visitor.lints_allowed.push(name_without_tool(&lint.to_string()).to_string());
141+
}
142+
}
143+
}
144+
145+
Lrc::new((visitor.lints_to_emit, visitor.lints_allowed))
146+
}
147+
118148
#[instrument(level = "trace", skip(tcx), ret)]
119149
fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap {
120150
let store = unerased_lint_store(tcx.sess);
@@ -301,6 +331,88 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> {
301331
}
302332
}
303333

334+
/// Visitor with the only function of visiting every item-like in a crate and
335+
/// computing the highest level that every lint gets put to.
336+
///
337+
/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item
338+
/// uses #[warn(lint)], this visitor will set that lint level as `Warn`
339+
struct LintLevelMinimum<'tcx> {
340+
tcx: TyCtxt<'tcx>,
341+
/// The actual list of detected lints.
342+
lints_to_emit: Vec<String>,
343+
lints_allowed: Vec<String>,
344+
}
345+
346+
impl<'tcx> LintLevelMinimum<'tcx> {
347+
pub fn new(tcx: TyCtxt<'tcx>) -> Self {
348+
Self {
349+
tcx,
350+
// That magic number is the current number of lints + some more for possible future lints
351+
lints_to_emit: Vec::with_capacity(230),
352+
lints_allowed: Vec::with_capacity(100),
353+
}
354+
}
355+
356+
fn process_opts(&mut self) {
357+
for (lint, level) in &self.tcx.sess.opts.lint_opts {
358+
if *level == Level::Allow {
359+
self.lints_allowed.push(lint.clone());
360+
} else {
361+
self.lints_to_emit.push(lint.to_string());
362+
}
363+
}
364+
}
365+
}
366+
367+
impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> {
368+
type NestedFilter = nested_filter::All;
369+
370+
fn nested_visit_map(&mut self) -> Self::Map {
371+
self.tcx.hir()
372+
}
373+
374+
fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) {
375+
if let Some(meta) = attribute.meta() {
376+
if [sym::warn, sym::deny, sym::forbid, sym::expect]
377+
.iter()
378+
.any(|kind| meta.has_name(*kind))
379+
{
380+
// SAFETY: Lint attributes are always a metalist inside a
381+
// metalist (even with just one lint).
382+
for meta_list in meta.meta_item_list().unwrap() {
383+
// If it's a tool lint (e.g. clippy::my_clippy_lint)
384+
if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list {
385+
if meta_item.path.segments.len() == 1 {
386+
self.lints_to_emit.push(
387+
// SAFETY: Lint attributes can only have literals
388+
meta_list.ident().unwrap().name.as_str().to_string(),
389+
);
390+
} else {
391+
self.lints_to_emit
392+
.push(meta_item.path.segments[1].ident.name.as_str().to_string());
393+
}
394+
}
395+
}
396+
// We handle #![allow]s differently, as these remove checking rather than adding.
397+
} else if meta.has_name(sym::allow)
398+
&& let ast::AttrStyle::Inner = attribute.style
399+
{
400+
for meta_list in meta.meta_item_list().unwrap() {
401+
// If it's a tool lint (e.g. clippy::my_clippy_lint)
402+
if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list {
403+
if meta_item.path.segments.len() == 1 {
404+
self.lints_allowed.push(meta_list.name_or_empty().as_str().to_string())
405+
} else {
406+
self.lints_allowed
407+
.push(meta_item.path.segments[1].ident.name.as_str().to_string());
408+
}
409+
}
410+
}
411+
}
412+
}
413+
}
414+
}
415+
304416
pub struct LintLevelsBuilder<'s, P> {
305417
sess: &'s Session,
306418
features: &'s Features,
@@ -931,7 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
9311043
}
9321044

9331045
pub(crate) fn provide(providers: &mut Providers) {
934-
*providers = Providers { shallow_lint_levels_on, ..*providers };
1046+
*providers =
1047+
Providers { shallow_lint_levels_on, lints_that_can_emit, ..*providers };
9351048
}
9361049

9371050
pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {

compiler/rustc_lint/src/lib.rs

+12-12
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,15 @@ early_lint_methods!(
170170
[
171171
pub BuiltinCombinedEarlyLintPass,
172172
[
173-
UnusedParens: UnusedParens::new(),
173+
UnusedParens: UnusedParens::default(),
174174
UnusedBraces: UnusedBraces,
175175
UnusedImportBraces: UnusedImportBraces,
176176
UnsafeCode: UnsafeCode,
177177
SpecialModuleName: SpecialModuleName,
178178
AnonymousParameters: AnonymousParameters,
179179
EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(),
180180
NonCamelCaseTypes: NonCamelCaseTypes,
181-
DeprecatedAttr: DeprecatedAttr::new(),
181+
DeprecatedAttr: DeprecatedAttr::default(),
182182
WhileTrue: WhileTrue,
183183
NonAsciiIdents: NonAsciiIdents,
184184
HiddenUnicodeCodepoints: HiddenUnicodeCodepoints,
@@ -601,25 +601,25 @@ fn register_builtins(store: &mut LintStore) {
601601
}
602602

603603
fn register_internals(store: &mut LintStore) {
604-
store.register_lints(&LintPassImpl::get_lints());
604+
store.register_lints(&LintPassImpl::default().get_lints());
605605
store.register_early_pass(|| Box::new(LintPassImpl));
606-
store.register_lints(&DefaultHashTypes::get_lints());
606+
store.register_lints(&DefaultHashTypes::default().get_lints());
607607
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
608-
store.register_lints(&QueryStability::get_lints());
608+
store.register_lints(&QueryStability::default().get_lints());
609609
store.register_late_mod_pass(|_| Box::new(QueryStability));
610-
store.register_lints(&ExistingDocKeyword::get_lints());
610+
store.register_lints(&ExistingDocKeyword::default().get_lints());
611611
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
612-
store.register_lints(&TyTyKind::get_lints());
612+
store.register_lints(&TyTyKind::default().get_lints());
613613
store.register_late_mod_pass(|_| Box::new(TyTyKind));
614-
store.register_lints(&TypeIr::get_lints());
614+
store.register_lints(&TypeIr::default().get_lints());
615615
store.register_late_mod_pass(|_| Box::new(TypeIr));
616-
store.register_lints(&Diagnostics::get_lints());
616+
store.register_lints(&Diagnostics::default().get_lints());
617617
store.register_late_mod_pass(|_| Box::new(Diagnostics));
618-
store.register_lints(&BadOptAccess::get_lints());
618+
store.register_lints(&BadOptAccess::default().get_lints());
619619
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
620-
store.register_lints(&PassByValue::get_lints());
620+
store.register_lints(&PassByValue::default().get_lints());
621621
store.register_late_mod_pass(|_| Box::new(PassByValue));
622-
store.register_lints(&SpanUseEqCtxt::get_lints());
622+
store.register_lints(&SpanUseEqCtxt::default().get_lints());
623623
store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt));
624624
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
625625
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and

compiler/rustc_lint/src/passes.rs

+8-2
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ macro_rules! declare_combined_late_lint_pass {
110110

111111
$v fn get_lints() -> $crate::LintVec {
112112
let mut lints = Vec::new();
113-
$(lints.extend_from_slice(&$pass::get_lints());)*
113+
$(lints.extend_from_slice(&$pass::default().get_lints());)*
114114
lints
115115
}
116116
}
@@ -124,6 +124,9 @@ macro_rules! declare_combined_late_lint_pass {
124124
fn name(&self) -> &'static str {
125125
panic!()
126126
}
127+
fn get_lints(&self) -> LintVec {
128+
panic!()
129+
}
127130
}
128131
)
129132
}
@@ -222,7 +225,7 @@ macro_rules! declare_combined_early_lint_pass {
222225

223226
$v fn get_lints() -> $crate::LintVec {
224227
let mut lints = Vec::new();
225-
$(lints.extend_from_slice(&$pass::get_lints());)*
228+
$(lints.extend_from_slice(&$pass::default().get_lints());)*
226229
lints
227230
}
228231
}
@@ -236,6 +239,9 @@ macro_rules! declare_combined_early_lint_pass {
236239
fn name(&self) -> &'static str {
237240
panic!()
238241
}
242+
fn get_lints(&self) -> LintVec {
243+
panic!()
244+
}
239245
}
240246
)
241247
}

0 commit comments

Comments
 (0)