Auto merge of #125116 - blyxyas:ignore-allowed-lints-final, r=cjgillot

(Big performance change) 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 change would improve both the Rust lint infrastructure and Clippy, but Clippy will see the most benefit, as it has about 900 registered lints (and growing!)

So yeah, with this little patch we filter all lints pre-linting, and remove any lint that is either:
- Manually `#![allow]`ed in the whole crate,
- Allowed in the command line, or
- Not manually enabled with `#[warn]` or similar, and its default level is `Allow`

As some lints **need** to run, this PR also adds **loadbearing lints**. On a lint declaration, you can use the ``@eval_always` = true` marker to label it as loadbearing. A loadbearing lint will never be filtered (it will always run)

Fixes #106983
This commit is contained in:
bors 2024-10-26 16:37:43 +00:00
commit 4d88de2acd
45 changed files with 291 additions and 56 deletions

View File

@ -4029,6 +4029,7 @@ dependencies = [
"rustc_hir",
"rustc_hir_pretty",
"rustc_index",
"rustc_lint_defs",
"rustc_macros",
"rustc_query_system",
"rustc_serialize",

View File

@ -223,7 +223,7 @@ impl AttrItem {
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
}
fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
pub fn meta_item_list(&self) -> Option<ThinVec<MetaItemInner>> {
match &self.args {
AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => {
MetaItemKind::list_from_tokens(args.tokens.clone())

View File

@ -73,7 +73,6 @@ use crate::{
EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext,
fluent_generated as fluent,
};
declare_lint! {
/// The `while_true` lint detects `while true { }`.
///
@ -241,7 +240,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",
@eval_always = true
}
declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]);
@ -389,6 +389,7 @@ declare_lint! {
report_in_external_macro
}
#[derive(Default)]
pub struct MissingDoc;
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
@ -819,8 +820,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() }
}
}

View File

@ -309,6 +309,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
fn name(&self) -> &'static str {
panic!()
}
fn get_lints(&self) -> crate::LintVec {
panic!()
}
}
macro_rules! impl_early_lint_pass {

View File

@ -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,
@eval_always = 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,
@eval_always = true
}
declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]);

View File

@ -26,11 +26,12 @@ use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::Session;
use rustc_session::lint::LintPass;
use rustc_session::lint::builtin::HardwiredLints;
use rustc_span::Span;
use tracing::debug;
use crate::passes::LateLintPassObject;
use crate::{LateContext, LateLintPass, LintStore};
use crate::{LateContext, LateLintPass, LintId, LintStore};
/// Extract the [`LintStore`] from [`Session`].
///
@ -326,6 +327,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 +365,20 @@ 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 builtin_lints = Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>;
let mut binding = store
.late_module_passes
.iter()
.map(|mk_pass| (mk_pass)(tcx))
.chain(std::iter::once(builtin_lints))
.collect::<Vec<_>>();
let pass = RuntimeCombinedLateLintPass { passes: binding.as_mut_slice() };
late_lint_mod_inner(tcx, module_def_id, context, pass);
}
}
@ -398,7 +409,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
// Note: `passes` is often empty.
let mut passes: Vec<_> =
let passes: Vec<_> =
unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
if passes.is_empty() {
@ -416,7 +427,18 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
only_module: false,
};
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(());
let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes
.into_iter()
.filter(|pass| {
let lints = (**pass).get_lints();
!lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint)))
})
.collect();
filtered_passes.push(Box::new(HardwiredLints));
let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] };
late_lint_crate_inner(tcx, context, pass);
}

View File

@ -1,9 +1,9 @@
use rustc_ast_pretty::pprust;
use rustc_data_structures::fx::FxIndexMap;
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{Diag, LintDiagnostic, MultiSpan};
use rustc_feature::{Features, GateIssue};
use rustc_hir::HirId;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{CRATE_HIR_ID, HirId};
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::hir::nested_filter;
@ -115,6 +115,38 @@ impl LintLevelSets {
}
}
fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet<LintId> {
let store = unerased_lint_store(&tcx.sess);
let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID);
let dont_need_to_run: FxIndexSet<LintId> = store
.get_lints()
.into_iter()
.filter_map(|lint| {
if !lint.eval_always {
let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID);
if matches!(lint_level, (Level::Allow, ..))
|| (matches!(lint_level, (.., LintLevelSource::Default)))
&& lint.default_level(tcx.sess.edition()) == Level::Allow
{
Some(LintId::of(lint))
} else {
None
}
} else {
None
}
})
.collect();
let mut visitor = LintLevelMaximum { tcx, dont_need_to_run };
visitor.process_opts();
tcx.hir().walk_attributes(&mut visitor);
visitor.dont_need_to_run
}
#[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 +333,83 @@ 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 LintLevelMaximum<'tcx> {
tcx: TyCtxt<'tcx>,
/// The actual list of detected lints.
dont_need_to_run: FxIndexSet<LintId>,
}
impl<'tcx> LintLevelMaximum<'tcx> {
fn process_opts(&mut self) {
let store = unerased_lint_store(self.tcx.sess);
for (lint_group, level) in &self.tcx.sess.opts.lint_opts {
if *level != Level::Allow {
let Ok(lints) = store.find_lints(lint_group) else {
return;
};
for lint in lints {
self.dont_need_to_run.swap_remove(&lint);
}
}
}
}
}
impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> {
type NestedFilter = nested_filter::All;
fn nested_visit_map(&mut self) -> Self::Map {
self.tcx.hir()
}
/// FIXME(blyxyas): In a future revision, we should also graph #![allow]s,
/// but that is handled with more care
fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) {
if matches!(
Level::from_attr(attribute),
Some(
Level::Warn
| Level::Deny
| Level::Forbid
| Level::Expect(..)
| Level::ForceWarn(..),
)
) {
let store = unerased_lint_store(self.tcx.sess);
let Some(meta) = attribute.meta() else { return };
// Lint attributes are always a metalist inside a
// metalist (even with just one lint).
let Some(meta_item_list) = meta.meta_item_list() else { return };
for meta_list in meta_item_list {
// Convert Path to String
let Some(meta_item) = meta_list.meta_item() else { return };
let ident: &str = &meta_item
.path
.segments
.iter()
.map(|segment| segment.ident.as_str())
.collect::<Vec<&str>>()
.join("::");
let Ok(lints) = store.find_lints(
// Lint attributes can only have literals
ident,
) else {
return;
};
for lint in lints {
self.dont_need_to_run.swap_remove(&lint);
}
}
}
}
}
pub struct LintLevelsBuilder<'s, P> {
sess: &'s Session,
features: &'s Features,
@ -934,7 +1043,7 @@ 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_dont_need_to_run, ..*providers };
}
pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) {

View File

@ -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,
@ -199,7 +199,6 @@ late_lint_methods!(
ForLoopsOverFallibles: ForLoopsOverFallibles,
DerefIntoDynSupertrait: DerefIntoDynSupertrait,
DropForgetUseless: DropForgetUseless,
HardwiredLints: HardwiredLints,
ImproperCTypesDeclarations: ImproperCTypesDeclarations,
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
InvalidFromUtf8: InvalidFromUtf8,
@ -280,6 +279,7 @@ fn register_builtins(store: &mut LintStore) {
store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints());
store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints());
store.register_lints(&foreign_modules::get_lints());
store.register_lints(&HardwiredLints::lint_vec());
add_lint_group!(
"nonstandard_style",
@ -602,25 +602,25 @@ fn register_builtins(store: &mut LintStore) {
}
fn register_internals(store: &mut LintStore) {
store.register_lints(&LintPassImpl::get_lints());
store.register_lints(&LintPassImpl::lint_vec());
store.register_early_pass(|| Box::new(LintPassImpl));
store.register_lints(&DefaultHashTypes::get_lints());
store.register_lints(&DefaultHashTypes::lint_vec());
store.register_late_mod_pass(|_| Box::new(DefaultHashTypes));
store.register_lints(&QueryStability::get_lints());
store.register_lints(&QueryStability::lint_vec());
store.register_late_mod_pass(|_| Box::new(QueryStability));
store.register_lints(&ExistingDocKeyword::get_lints());
store.register_lints(&ExistingDocKeyword::lint_vec());
store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword));
store.register_lints(&TyTyKind::get_lints());
store.register_lints(&TyTyKind::lint_vec());
store.register_late_mod_pass(|_| Box::new(TyTyKind));
store.register_lints(&TypeIr::get_lints());
store.register_lints(&TypeIr::lint_vec());
store.register_late_mod_pass(|_| Box::new(TypeIr));
store.register_lints(&Diagnostics::get_lints());
store.register_lints(&Diagnostics::lint_vec());
store.register_late_mod_pass(|_| Box::new(Diagnostics));
store.register_lints(&BadOptAccess::get_lints());
store.register_lints(&BadOptAccess::lint_vec());
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
store.register_lints(&PassByValue::get_lints());
store.register_lints(&PassByValue::lint_vec());
store.register_late_mod_pass(|_| Box::new(PassByValue));
store.register_lints(&SpanUseEqCtxt::get_lints());
store.register_lints(&SpanUseEqCtxt::lint_vec());
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

View File

@ -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::lint_vec());)*
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::lint_vec());)*
lints
}
}
@ -236,6 +239,9 @@ macro_rules! declare_combined_early_lint_pass {
fn name(&self) -> &'static str {
panic!()
}
fn get_lints(&self) -> LintVec {
panic!()
}
}
)
}

View File

@ -167,7 +167,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>,

View File

@ -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 {
fn default() -> Self {
Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() }
}
}

View File

@ -377,7 +377,8 @@ declare_lint! {
/// will not overflow.
pub ARITHMETIC_OVERFLOW,
Deny,
"arithmetic operation overflows"
"arithmetic operation overflows",
@eval_always = true
}
declare_lint! {
@ -401,7 +402,8 @@ declare_lint! {
/// `panic!` or `unreachable!` macro instead in case the panic is intended.
pub UNCONDITIONAL_PANIC,
Deny,
"operation will cause a panic at runtime"
"operation will cause a panic at runtime",
@eval_always = true
}
declare_lint! {
@ -632,7 +634,8 @@ declare_lint! {
/// is only available in a newer version.
pub UNKNOWN_LINTS,
Warn,
"unrecognized lint attribute"
"unrecognized lint attribute",
@eval_always = true
}
declare_lint! {

View File

@ -312,6 +312,10 @@ pub struct Lint {
pub feature_gate: Option<Symbol>,
pub crate_level_only: bool,
/// `true` if this lint should not be filtered out under any circustamces
/// (e.g. the unknown_attributes lint)
pub eval_always: bool,
}
/// Extra information for a future incompatibility lint.
@ -456,6 +460,7 @@ impl Lint {
future_incompatible: None,
feature_gate: None,
crate_level_only: false,
eval_always: false,
}
}
@ -864,6 +869,7 @@ macro_rules! declare_lint {
);
);
($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr,
$(@eval_always = $eval_always:literal)?
$(@feature_gate = $gate:ident;)?
$(@future_incompatible = FutureIncompatibleInfo {
reason: $reason:expr,
@ -885,6 +891,7 @@ macro_rules! declare_lint {
..$crate::FutureIncompatibleInfo::default_fields_for_macro()
}),)?
$(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)?
$(eval_always: $eval_always,)?
..$crate::Lint::default_fields_for_macro()
};
);
@ -894,20 +901,23 @@ macro_rules! declare_lint {
macro_rules! declare_tool_lint {
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr
$(, @eval_always = $eval_always:literal)?
$(, @feature_gate = $gate:ident;)?
) => (
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @feature_gate = $gate;)?}
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?}
);
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
report_in_external_macro: $rep:expr
$(, @eval_always = $eval_always: literal)?
$(, @feature_gate = $gate:ident;)?
) => (
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?}
$crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?}
);
(
$(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr,
$external:expr
$(, @eval_always = $eval_always: literal)?
$(, @feature_gate = $gate:ident;)?
) => (
$(#[$attr])*
@ -921,6 +931,7 @@ macro_rules! declare_tool_lint {
is_externally_loaded: true,
$(feature_gate: Some(rustc_span::symbol::sym::$gate),)?
crate_level_only: false,
$(eval_always: $eval_always,)?
..$crate::Lint::default_fields_for_macro()
};
);
@ -930,6 +941,7 @@ pub type LintVec = Vec<&'static Lint>;
pub trait LintPass {
fn name(&self) -> &'static str;
fn get_lints(&self) -> LintVec;
}
/// Implements `LintPass for $ty` with the given list of `Lint` statics.
@ -938,9 +950,11 @@ macro_rules! impl_lint_pass {
($ty:ty => [$($lint:expr),* $(,)?]) => {
impl $crate::LintPass for $ty {
fn name(&self) -> &'static str { stringify!($ty) }
fn get_lints(&self) -> $crate::LintVec { vec![$($lint),*] }
}
impl $ty {
pub fn get_lints() -> $crate::LintVec { vec![$($lint),*] }
#[allow(unused)]
pub fn lint_vec() -> $crate::LintVec { vec![$($lint),*] }
}
};
}

View File

@ -27,6 +27,7 @@ rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
rustc_hir_pretty = { path = "../rustc_hir_pretty" }
rustc_index = { path = "../rustc_index" }
rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_serialize = { path = "../rustc_serialize" }

View File

@ -29,6 +29,7 @@ use rustc_hir::def_id::{
use rustc_hir::lang_items::{LangItem, LanguageItems};
use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate};
use rustc_index::IndexVec;
use rustc_lint_defs::LintId;
use rustc_macros::rustc_queries;
use rustc_query_system::ich::StableHashingContext;
use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cached};
@ -422,6 +423,11 @@ rustc_queries! {
desc { "computing `#[expect]`ed lints in this crate" }
}
query lints_that_dont_need_to_run(_: ()) -> &'tcx FxIndexSet<LintId> {
arena_cache
desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" }
}
query expn_that_defined(key: DefId) -> rustc_span::ExpnId {
desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) }
separate_provide_extern

View File

@ -31,9 +31,9 @@ where
allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned());
let lints = || {
lint::builtin::HardwiredLints::get_lints()
lint::builtin::HardwiredLints::lint_vec()
.into_iter()
.chain(rustc_lint::SoftLints::get_lints())
.chain(rustc_lint::SoftLints::lint_vec())
};
let lint_opts = lints()

View File

@ -31,6 +31,7 @@ declare_clippy_lint! {
pub COGNITIVE_COMPLEXITY,
nursery,
"functions that should be split up into multiple functions"
@eval_always = true
}
pub struct CognitiveComplexity {

View File

@ -0,0 +1,40 @@
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::FnKind;
use rustc_hir::{Body, FnDecl};
use rustc_lint::Level::Deny;
use rustc_lint::{LateContext, LateLintPass, Lint};
use rustc_session::declare_lint_pass;
use rustc_span::Span;
/// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
/// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
pub static CLIPPY_CTFE: &Lint = &Lint {
name: &"clippy::CLIPPY_CTFE",
default_level: Deny,
desc: "Ensure CTFE is being made",
edition_lint_opts: None,
report_in_external_macro: true,
future_incompatible: None,
is_externally_loaded: true,
crate_level_only: false,
eval_always: true,
..Lint::default_fields_for_macro()
};
// No static CLIPPY_CTFE_INFO because we want this lint to be invisible
declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] }
impl<'tcx> LateLintPass<'tcx> for ClippyCtfe {
fn check_fn(
&mut self,
cx: &LateContext<'_>,
_: FnKind<'tcx>,
_: &'tcx FnDecl<'tcx>,
_: &'tcx Body<'tcx>,
_: Span,
defid: LocalDefId,
) {
cx.tcx.ensure().mir_drops_elaborated_and_const_checked(defid); // Lint
}
}

View File

@ -9,6 +9,7 @@ macro_rules! declare_clippy_lint {
$desc:literal,
$version_expr:expr,
$version_lit:literal
$(, $eval_always: literal)?
) => {
rustc_session::declare_tool_lint! {
$(#[doc = $lit])*
@ -17,6 +18,7 @@ macro_rules! declare_clippy_lint {
$category,
$desc,
report_in_external_macro:true
$(, @eval_always = $eval_always)?
}
pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
@ -33,11 +35,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
restriction,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -46,12 +49,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
style,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Warn, crate::LintCategory::Style, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -60,11 +63,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
correctness,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
@ -74,11 +78,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
perf,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -87,11 +92,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
complexity,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -100,11 +106,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
suspicious,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -113,11 +120,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
nursery,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -126,11 +134,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
pedantic,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};
(
@ -139,11 +148,12 @@ macro_rules! declare_clippy_lint {
pub $lint_name:ident,
cargo,
$desc:literal
$(@eval_always = $eval_always: literal)?
) => {
declare_clippy_lint! {@
$(#[doc = $lit])*
pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
Some($version), $version
Some($version), $version $(, $eval_always)?
}
};

View File

@ -65,6 +65,7 @@ extern crate clippy_utils;
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
mod utils;
pub mod ctfe; // Very important lint, do not remove (rust#125116)
pub mod declared_lints;
pub mod deprecated_lints;
@ -605,6 +606,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
});
}
store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe));
store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf)));
store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
store.register_late_pass(|_| Box::new(utils::author::Author));

View File

@ -1,3 +1,5 @@
#![warn(clippy::author)]
fn main() {
#[clippy::author]
let x: char = 0x45 as char;

View File

@ -0,0 +1,3 @@
#![deny(clippy::all)]
fn main() {}

View File

@ -6,7 +6,7 @@
extern crate rustc_middle;
extern crate rustc_session;
use rustc_session::lint::{LintPass, LintVec};
use rustc_session::lint::{LintPass, LintVec, Lint};
use rustc_session::{declare_lint, declare_lint_pass, impl_lint_pass};
declare_lint! {
@ -21,6 +21,10 @@ impl LintPass for Foo { //~ERROR implementing `LintPass` by hand
fn name(&self) -> &'static str {
"Foo"
}
fn get_lints(&self) -> Vec<&'static Lint> {
vec![TEST_LINT]
}
}
macro_rules! custom_lint_pass_macro {
@ -31,6 +35,10 @@ macro_rules! custom_lint_pass_macro {
fn name(&self) -> &'static str {
"Custom"
}
fn get_lints(&self) -> Vec<&'static Lint> {
vec![TEST_LINT]
}
}
};
}

View File

@ -12,7 +12,7 @@ LL | #![deny(rustc::lint_pass_impl_without_macro)]
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: implementing `LintPass` by hand
--> $DIR/lint_pass_impl_without_macro.rs:30:14
--> $DIR/lint_pass_impl_without_macro.rs:34:14
|
LL | impl LintPass for Custom {
| ^^^^^^^^