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

45 files changed

Lines changed: 264 additions & 50 deletions

Some content is hidden

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

‎Cargo.lock‎

Lines changed: 1 addition & 0 deletions
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‎

Lines changed: 1 addition & 1 deletion
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-
fnmeta_item_list(&self) -> Option<ThinVec<MetaItemInner>>{
226+
pubfnmeta_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‎

Lines changed: 11 additions & 3 deletions
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+
pubuse 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
pubstructMissingDoc;
393401

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

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

822-
implDeprecatedAttr{
823-
pubfnnew() -> DeprecatedAttr{
830+
implDefaultforDeprecatedAttr{
831+
fndefault() -> Self{
824832
DeprecatedAttr{depr_attrs:deprecated_attributes()}
825833
}
826834
}

‎compiler/rustc_lint/src/early.rs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
312312
fnname(&self) -> &'staticstr{
313313
panic!()
314314
}
315+
fnget_lints(&self) -> crate::LintVec{
316+
panic!()
317+
}
315318
}
316319

317320
macro_rules! impl_early_lint_pass {

‎compiler/rustc_lint/src/internal.rs‎

Lines changed: 4 additions & 2 deletions
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‎

Lines changed: 40 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
326326
fnname(&self) -> &'staticstr{
327327
panic!()
328328
}
329+
fnget_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-
letmut 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+
letmut filtered_passes:Vec<Box<dynLateLintPass<'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)fnname_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‎

Lines changed: 117 additions & 4 deletions
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
usecrate::fluent_generated as fluent;
34-
usecrate::late::unerased_lint_store;
34+
usecrate::late::{unerased_lint_store, name_without_tool};
3535
usecrate::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+
pubfnlints_that_can_emit(tcx:TyCtxt<'_>,():()) -> Lrc<(Vec<String>,Vec<String>)>{
124+
letmut 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+
}elseif 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
fnshallow_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+
structLintLevelMinimum<'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+
pubfnnew(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+
fnprocess_opts(&mutself){
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>forLintLevelMinimum<'tcx>{
368+
typeNestedFilter = nested_filter::All;
369+
370+
fnnested_visit_map(&mutself) -> Self::Map{
371+
self.tcx.hir()
372+
}
373+
374+
fnvisit_attribute(&mutself,attribute:&'tcx ast::Attribute){
375+
ifletSome(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+
iflet 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+
}elseif 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+
iflet 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
pubstructLintLevelsBuilder<'s,P>{
305417
sess:&'sSession,
306418
features:&'sFeatures,
@@ -931,7 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> {
9311043
}
9321044

9331045
pub(crate)fnprovide(providers:&mutProviders){
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)fnparse_lint_and_tool_name(lint_name:&str) -> (Option<Symbol>,&str){

‎compiler/rustc_lint/src/lib.rs‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,15 @@ early_lint_methods!(
170170
[
171171
pubBuiltinCombinedEarlyLintPass,
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
fnregister_internals(store:&mutLintStore){
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‎

Lines changed: 8 additions & 2 deletions
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
letmut 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) -> &'staticstr{
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
letmut 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) -> &'staticstr{
237240
panic!()
238241
}
242+
fn get_lints(&self) -> LintVec{
243+
panic!()
244+
}
239245
}
240246
)
241247
}

0 commit comments

Comments
 (0)