Prepare to split lints into multiple crates

* Move `declare_clippy_lint` to it's own crate
* Move lint/group registration into the driver
* Make `dev update_lints` handle multiple lint crates
This commit is contained in:
Jason Newcomb 2025-04-23 20:04:35 -04:00
parent 19c1c70905
commit acd8810e77
19 changed files with 485 additions and 436 deletions

View file

@ -24,6 +24,7 @@ path = "src/driver.rs"
clippy_config = { path = "clippy_config" }
clippy_lints = { path = "clippy_lints" }
clippy_utils = { path = "clippy_utils" }
declare_clippy_lint = { path = "declare_clippy_lint" }
rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
tempfile = { version = "3.20", optional = true }

View file

@ -5,6 +5,7 @@ static CARGO_TOML_FILES: &[&str] = &[
"clippy_config/Cargo.toml",
"clippy_lints/Cargo.toml",
"clippy_utils/Cargo.toml",
"declare_clippy_lint/Cargo.toml",
"Cargo.toml",
];

View file

@ -4,8 +4,9 @@ use crate::utils::{
use itertools::Itertools;
use std::collections::HashSet;
use std::fmt::Write;
use std::fs;
use std::ops::Range;
use std::path::{Path, PathBuf};
use std::path::{self, Path, PathBuf};
use walkdir::{DirEntry, WalkDir};
const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
@ -36,123 +37,164 @@ pub fn generate_lint_files(
deprecated: &[DeprecatedLint],
renamed: &[RenamedLint],
) {
FileUpdater::default().update_files_checked(
let mut updater = FileUpdater::default();
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
&mut [
(
"README.md",
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
}),
),
(
"book/src/README.md",
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
}),
),
(
"CHANGELOG.md",
&mut update_text_region_fn(
"<!-- begin autogenerated links to lint list -->\n",
"<!-- end autogenerated links to lint list -->",
|dst| {
for lint in lints
.iter()
.map(|l| &*l.name)
.chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
.chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
.sorted()
{
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
}
},
),
),
(
"clippy_lints/src/lib.rs",
&mut update_text_region_fn(
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|dst| {
for lint_mod in lints.iter().map(|l| &l.module).sorted().dedup() {
writeln!(dst, "mod {lint_mod};").unwrap();
}
},
),
),
("clippy_lints/src/declared_lints.rs", &mut |_, src, dst| {
dst.push_str(GENERATED_FILE_COMMENT);
dst.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
for (module_name, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
writeln!(dst, " crate::{module_name}::{lint_name}_INFO,").unwrap();
"README.md",
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
}),
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
"book/src/README.md",
&mut update_text_region_fn("[There are over ", " lints included in this crate!]", |dst| {
write!(dst, "{}", round_to_fifty(lints.len())).unwrap();
}),
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
"CHANGELOG.md",
&mut update_text_region_fn(
"<!-- begin autogenerated links to lint list -->\n",
"<!-- end autogenerated links to lint list -->",
|dst| {
for lint in lints
.iter()
.map(|l| &*l.name)
.chain(deprecated.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
.chain(renamed.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
.sorted()
{
writeln!(dst, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
}
dst.push_str("];\n");
UpdateStatus::from_changed(src != dst)
}),
("clippy_lints/src/deprecated_lints.rs", &mut |_, src, dst| {
let mut searcher = RustSearcher::new(src);
assert!(
searcher.find_token(Token::Ident("declare_with_version"))
&& searcher.find_token(Token::Ident("declare_with_version")),
"error reading deprecated lints"
);
dst.push_str(&src[..searcher.pos() as usize]);
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
for lint in deprecated {
write!(
dst,
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
lint.version, lint.name, lint.reason,
)
.unwrap();
}
dst.push_str(
"]}\n\n\
},
),
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
"clippy_lints/src/deprecated_lints.rs",
&mut |_, src, dst| {
let mut searcher = RustSearcher::new(src);
assert!(
searcher.find_token(Token::Ident("declare_with_version"))
&& searcher.find_token(Token::Ident("declare_with_version")),
"error reading deprecated lints"
);
dst.push_str(&src[..searcher.pos() as usize]);
dst.push_str("! { DEPRECATED(DEPRECATED_VERSION) = [\n");
for lint in deprecated {
write!(
dst,
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
lint.version, lint.name, lint.reason,
)
.unwrap();
}
dst.push_str(
"]}\n\n\
#[rustfmt::skip]\n\
declare_with_version! { RENAMED(RENAMED_VERSION) = [\n\
",
);
for lint in renamed {
write!(
dst,
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
lint.version, lint.old_name, lint.new_name,
)
.unwrap();
}
dst.push_str("]}\n");
UpdateStatus::from_changed(src != dst)
}),
("tests/ui/deprecated.rs", &mut |_, src, dst| {
dst.push_str(GENERATED_FILE_COMMENT);
for lint in deprecated {
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
}
dst.push_str("\nfn main() {}\n");
UpdateStatus::from_changed(src != dst)
}),
("tests/ui/rename.rs", &mut move |_, src, dst| {
let mut seen_lints = HashSet::new();
dst.push_str(GENERATED_FILE_COMMENT);
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
for lint in renamed {
if seen_lints.insert(&lint.new_name) {
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
}
}
seen_lints.clear();
for lint in renamed {
if seen_lints.insert(&lint.old_name) {
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
}
}
dst.push_str("\nfn main() {}\n");
UpdateStatus::from_changed(src != dst)
}),
],
);
for lint in renamed {
write!(
dst,
" #[clippy::version = \"{}\"]\n (\"{}\", \"{}\"),\n",
lint.version, lint.old_name, lint.new_name,
)
.unwrap();
}
dst.push_str("]}\n");
UpdateStatus::from_changed(src != dst)
},
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
"tests/ui/deprecated.rs",
&mut |_, src, dst| {
dst.push_str(GENERATED_FILE_COMMENT);
for lint in deprecated {
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
}
dst.push_str("\nfn main() {}\n");
UpdateStatus::from_changed(src != dst)
},
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
"tests/ui/rename.rs",
&mut move |_, src, dst| {
let mut seen_lints = HashSet::new();
dst.push_str(GENERATED_FILE_COMMENT);
dst.push_str("#![allow(clippy::duplicated_attributes)]\n");
for lint in renamed {
if seen_lints.insert(&lint.new_name) {
writeln!(dst, "#![allow({})]", lint.new_name).unwrap();
}
}
seen_lints.clear();
for lint in renamed {
if seen_lints.insert(&lint.old_name) {
writeln!(dst, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
}
}
dst.push_str("\nfn main() {}\n");
UpdateStatus::from_changed(src != dst)
},
);
for (crate_name, lints) in lints.iter().into_group_map_by(|&l| {
let Some(path::Component::Normal(name)) = l.path.components().next() else {
// All paths should start with `{crate_name}/src` when parsed from `find_lint_decls`
panic!("internal error: can't read crate name from path `{}`", l.path.display());
};
name
}) {
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
Path::new(crate_name).join("src/lib.rs"),
&mut update_text_region_fn(
"// begin lints modules, do not remove this comment, it's used in `update_lints`\n",
"// end lints modules, do not remove this comment, it's used in `update_lints`",
|dst| {
for lint_mod in lints
.iter()
.filter(|l| !l.module.is_empty())
.map(|l| l.module.split_once("::").map_or(&*l.module, |x| x.0))
.sorted()
.dedup()
{
writeln!(dst, "mod {lint_mod};").unwrap();
}
},
),
);
updater.update_file_checked(
"cargo dev update_lints",
update_mode,
Path::new(crate_name).join("src/declared_lints.rs"),
&mut |_, src, dst| {
dst.push_str(GENERATED_FILE_COMMENT);
dst.push_str("pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[\n");
for (module_path, lint_name) in lints.iter().map(|l| (&l.module, l.name.to_uppercase())).sorted() {
if module_path.is_empty() {
writeln!(dst, " crate::{lint_name}_INFO,").unwrap();
} else {
writeln!(dst, " crate::{module_path}::{lint_name}_INFO,").unwrap();
}
}
dst.push_str("];\n");
UpdateStatus::from_changed(src != dst)
},
);
}
}
fn round_to_fifty(count: usize) -> usize {
@ -186,13 +228,25 @@ pub struct RenamedLint {
pub fn find_lint_decls() -> Vec<Lint> {
let mut lints = Vec::with_capacity(1000);
let mut contents = String::new();
for (file, module) in read_src_with_module("clippy_lints/src".as_ref()) {
parse_clippy_lint_decls(
file.path(),
File::open_read_to_cleared_string(file.path(), &mut contents),
&module,
&mut lints,
);
for e in expect_action(fs::read_dir("."), ErrAction::Read, ".") {
let e = expect_action(e, ErrAction::Read, ".");
if !expect_action(e.file_type(), ErrAction::Read, ".").is_dir() {
continue;
}
let Ok(mut name) = e.file_name().into_string() else {
continue;
};
if name.starts_with("clippy_lints") && name != "clippy_lints_internal" {
name.push_str("/src");
for (file, module) in read_src_with_module(name.as_ref()) {
parse_clippy_lint_decls(
file.path(),
File::open_read_to_cleared_string(file.path(), &mut contents),
&module,
&mut lints,
);
}
}
}
lints.sort_by(|lhs, rhs| lhs.name.cmp(&rhs.name));
lints
@ -204,7 +258,7 @@ fn read_src_with_module(src_root: &Path) -> impl use<'_> + Iterator<Item = (DirE
let e = expect_action(e, ErrAction::Read, src_root);
let path = e.path().as_os_str().as_encoded_bytes();
if let Some(path) = path.strip_suffix(b".rs")
&& let Some(path) = path.get("clippy_lints/src/".len()..)
&& let Some(path) = path.get(src_root.as_os_str().len() + 1..)
{
if path == b"lib" {
Some((e, String::new()))

View file

@ -383,21 +383,6 @@ impl FileUpdater {
self.update_file_checked_inner(tool, mode, path.as_ref(), update);
}
#[expect(clippy::type_complexity)]
pub fn update_files_checked(
&mut self,
tool: &str,
mode: UpdateMode,
files: &mut [(
impl AsRef<Path>,
&mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus,
)],
) {
for (path, update) in files {
self.update_file_checked_inner(tool, mode, path.as_ref(), update);
}
}
pub fn update_file(
&mut self,
path: impl AsRef<Path>,

View file

@ -13,6 +13,7 @@ arrayvec = { version = "0.7", default-features = false }
cargo_metadata = "0.18"
clippy_config = { path = "../clippy_config" }
clippy_utils = { path = "../clippy_utils" }
declare_clippy_lint = { path = "../declare_clippy_lint" }
itertools = "0.12"
quine-mc_cluskey = "0.2"
regex-syntax = "0.8"

View file

@ -1,168 +0,0 @@
#[macro_export]
#[allow(clippy::crate_in_macro_def)]
macro_rules! declare_clippy_lint {
(@
$(#[doc = $lit:literal])*
pub $lint_name:ident,
$level:ident,
$lintcategory:expr,
$desc:literal,
$version_expr:expr,
$version_lit:literal
$(, $eval_always: literal)?
) => {
rustc_session::declare_tool_lint! {
$(#[doc = $lit])*
#[clippy::version = $version_lit]
pub clippy::$lint_name,
$level,
$desc,
report_in_external_macro:true
$(, @eval_always = $eval_always)?
}
pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo {
lint: &$lint_name,
category: $lintcategory,
explanation: concat!($($lit,"\n",)*),
location: concat!(file!(), "#L", line!()),
version: $version_expr
};
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
(
$(#[doc = $lit:literal])*
#[clippy::version = $version:literal]
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
$(, $eval_always)?
}
};
}

View file

@ -2,7 +2,7 @@
// Use that command to update this file and do not edit by hand.
// Manual edits will be overwritten.
pub static LINTS: &[&crate::LintInfo] = &[
pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
crate::absolute_paths::ABSOLUTE_PATHS_INFO,
crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
crate::approx_const::APPROX_CONSTANT_INFO,

View file

@ -5,14 +5,13 @@ use clippy_utils::{
eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
numeric_literal, peel_blocks, sugg, sym,
};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty;
use rustc_session::declare_lint_pass;
use rustc_span::source_map::Spanned;
use rustc_ast::ast;
use std::f32::consts as f32_consts;
use std::f64::consts as f64_consts;
use sugg::Sugg;

View file

@ -59,10 +59,10 @@ extern crate smallvec;
extern crate thin_vec;
#[macro_use]
mod declare_clippy_lint;
extern crate clippy_utils;
#[macro_use]
extern crate clippy_utils;
extern crate declare_clippy_lint;
mod utils;
@ -411,108 +411,9 @@ mod zombie_processes;
use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
use clippy_utils::macros::FormatArgsStorage;
use rustc_data_structures::fx::FxHashSet;
use rustc_lint::{Lint, LintId};
use rustc_lint::Lint;
use utils::attr_collector::{AttrCollector, AttrStorage};
#[derive(Default)]
struct RegistrationGroups {
all: Vec<LintId>,
cargo: Vec<LintId>,
complexity: Vec<LintId>,
correctness: Vec<LintId>,
nursery: Vec<LintId>,
pedantic: Vec<LintId>,
perf: Vec<LintId>,
restriction: Vec<LintId>,
style: Vec<LintId>,
suspicious: Vec<LintId>,
}
impl RegistrationGroups {
#[rustfmt::skip]
fn register(self, store: &mut rustc_lint::LintStore) {
store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
store.register_group(true, "clippy::correctness", Some("clippy_correctness"), self.correctness);
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), self.restriction);
store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
}
}
#[derive(Copy, Clone, Debug)]
pub(crate) enum LintCategory {
Cargo,
Complexity,
Correctness,
Nursery,
Pedantic,
Perf,
Restriction,
Style,
Suspicious,
}
#[allow(clippy::enum_glob_use)]
use LintCategory::*;
impl LintCategory {
fn is_all(self) -> bool {
matches!(self, Correctness | Suspicious | Style | Complexity | Perf)
}
fn group(self, groups: &mut RegistrationGroups) -> &mut Vec<LintId> {
match self {
Cargo => &mut groups.cargo,
Complexity => &mut groups.complexity,
Correctness => &mut groups.correctness,
Nursery => &mut groups.nursery,
Pedantic => &mut groups.pedantic,
Perf => &mut groups.perf,
Restriction => &mut groups.restriction,
Style => &mut groups.style,
Suspicious => &mut groups.suspicious,
}
}
}
pub struct LintInfo {
/// Double reference to maintain pointer equality
pub lint: &'static &'static Lint,
category: LintCategory,
pub explanation: &'static str,
/// e.g. `clippy_lints/src/absolute_paths.rs#43`
pub location: &'static str,
pub version: Option<&'static str>,
}
impl LintInfo {
/// Returns the lint name in lowercase without the `clippy::` prefix
#[allow(clippy::missing_panics_doc)]
pub fn name_lower(&self) -> String {
self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
}
/// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
pub fn category_str(&self) -> &'static str {
match self.category {
Cargo => "cargo",
Complexity => "complexity",
Correctness => "correctness",
Nursery => "nursery",
Pedantic => "pedantic",
Perf => "perf",
Restriction => "restriction",
Style => "style",
Suspicious => "suspicious",
}
}
}
pub fn explain(name: &str) -> i32 {
let target = format!("clippy::{}", name.to_ascii_uppercase());
@ -535,30 +436,11 @@ pub fn explain(name: &str) -> i32 {
}
}
fn register_categories(store: &mut rustc_lint::LintStore) {
let mut groups = RegistrationGroups::default();
for LintInfo { lint, category, .. } in declared_lints::LINTS {
if category.is_all() {
groups.all.push(LintId::of(lint));
}
category.group(&mut groups).push(LintId::of(lint));
}
let lints: Vec<&'static Lint> = declared_lints::LINTS.iter().map(|info| *info.lint).collect();
store.register_lints(&lints);
groups.register(store);
}
/// Register all lints and lint groups with the rustc lint store
///
/// Used in `./src/driver.rs`.
#[expect(clippy::too_many_lines)]
pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
register_categories(store);
pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
for (old_name, new_name) in deprecated_lints::RENAMED {
store.register_renamed(old_name, new_name);
}

View file

@ -13,7 +13,6 @@ use rustc_errors::Applicability;
use rustc_hir::def::{CtorOf, DefKind, Res};
use rustc_hir::{Arm, Expr, ExprKind, HirId, MatchSource, Pat, PatExpr, PatExprKind, PatKind, QPath, Stmt, StmtKind};
use rustc_lint::{LateContext, LintContext};
use rustc_span::Span;
use rustc_span::symbol::{Symbol, sym};
use std::slice;

View file

@ -5,7 +5,6 @@ use clippy_utils::source::{snippet, snippet_with_applicability};
use rustc_ast::ast;
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;

View file

@ -1,5 +1,4 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::macros::span_is_local;
use rustc_hir::{Expr, ExprKind, MatchSource};
use rustc_lint::{LateContext, LateLintPass};

View file

@ -3,11 +3,10 @@ use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
use clippy_utils::source::snippet;
use clippy_utils::{get_enclosing_block, sym};
use hir::{Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
use rustc_errors::Applicability;
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::intravisit::{Visitor, walk_expr};
use rustc_hir::{self as hir, Expr, ExprKind, HirId, LetStmt, PatKind, PathSegment, QPath, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::declare_lint_pass;

View file

@ -0,0 +1,10 @@
[package]
name = "declare_clippy_lint"
version = "0.1.89"
edition = "2024"
repository = "https://github.com/rust-lang/rust-clippy"
license = "MIT OR Apache-2.0"
[package.metadata.rust-analyzer]
# This crate uses #[feature(rustc_private)]
rustc_private = true

View file

@ -0,0 +1,280 @@
#![feature(macro_metavar_expr_concat, rustc_private)]
extern crate rustc_lint;
use rustc_lint::{Lint, LintId, LintStore};
// Needed by `declare_clippy_lint!`.
pub extern crate rustc_session;
#[derive(Default)]
pub struct LintListBuilder {
lints: Vec<&'static Lint>,
all: Vec<LintId>,
cargo: Vec<LintId>,
complexity: Vec<LintId>,
correctness: Vec<LintId>,
nursery: Vec<LintId>,
pedantic: Vec<LintId>,
perf: Vec<LintId>,
restriction: Vec<LintId>,
style: Vec<LintId>,
suspicious: Vec<LintId>,
}
impl LintListBuilder {
pub fn insert(&mut self, lints: &[&LintInfo]) {
#[allow(clippy::enum_glob_use)]
use LintCategory::*;
self.lints.extend(lints.iter().map(|&x| x.lint));
for &&LintInfo { lint, category, .. } in lints {
let (all, cat) = match category {
Complexity => (Some(&mut self.all), &mut self.complexity),
Correctness => (Some(&mut self.all), &mut self.correctness),
Perf => (Some(&mut self.all), &mut self.perf),
Style => (Some(&mut self.all), &mut self.style),
Suspicious => (Some(&mut self.all), &mut self.suspicious),
Cargo => (None, &mut self.cargo),
Nursery => (None, &mut self.nursery),
Pedantic => (None, &mut self.pedantic),
Restriction => (None, &mut self.restriction),
};
if let Some(all) = all {
all.push(LintId::of(lint));
}
cat.push(LintId::of(lint));
}
}
pub fn register(self, store: &mut LintStore) {
store.register_lints(&self.lints);
store.register_group(true, "clippy::all", Some("clippy_all"), self.all);
store.register_group(true, "clippy::cargo", Some("clippy_cargo"), self.cargo);
store.register_group(true, "clippy::complexity", Some("clippy_complexity"), self.complexity);
store.register_group(
true,
"clippy::correctness",
Some("clippy_correctness"),
self.correctness,
);
store.register_group(true, "clippy::nursery", Some("clippy_nursery"), self.nursery);
store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), self.pedantic);
store.register_group(true, "clippy::perf", Some("clippy_perf"), self.perf);
store.register_group(
true,
"clippy::restriction",
Some("clippy_restriction"),
self.restriction,
);
store.register_group(true, "clippy::style", Some("clippy_style"), self.style);
store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), self.suspicious);
}
}
#[derive(Copy, Clone, Debug)]
pub enum LintCategory {
Cargo,
Complexity,
Correctness,
Nursery,
Pedantic,
Perf,
Restriction,
Style,
Suspicious,
}
impl LintCategory {
#[must_use]
pub fn name(self) -> &'static str {
match self {
Self::Cargo => "cargo",
Self::Complexity => "complexity",
Self::Correctness => "correctness",
Self::Nursery => "nursery",
Self::Pedantic => "pedantic",
Self::Perf => "perf",
Self::Restriction => "restriction",
Self::Style => "style",
Self::Suspicious => "suspicious",
}
}
}
pub struct LintInfo {
pub lint: &'static Lint,
pub category: LintCategory,
pub explanation: &'static str,
/// e.g. `clippy_lints/src/absolute_paths.rs#43`
pub location: &'static str,
pub version: &'static str,
}
impl LintInfo {
/// Returns the lint name in lowercase without the `clippy::` prefix
#[must_use]
#[expect(clippy::missing_panics_doc)]
pub fn name_lower(&self) -> String {
self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
}
}
#[macro_export]
macro_rules! declare_clippy_lint_inner {
(
$(#[doc = $docs:literal])*
#[clippy::version = $version:literal]
$vis:vis $lint_name:ident,
$level:ident,
$category:ident,
$desc:literal
$(, @eval_always = $eval_always:literal)?
) => {
$crate::rustc_session::declare_tool_lint! {
$(#[doc = $docs])*
#[clippy::version = $version]
$vis clippy::$lint_name,
$level,
$desc,
report_in_external_macro:true
$(, @eval_always = $eval_always)?
}
pub(crate) static ${concat($lint_name, _INFO)}: &'static $crate::LintInfo = &$crate::LintInfo {
lint: $lint_name,
category: $crate::LintCategory::$category,
explanation: concat!($($docs,"\n",)*),
location: concat!(file!(), "#L", line!()),
version: $version,
};
};
}
#[macro_export]
macro_rules! declare_clippy_lint {
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
correctness,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Deny,
Correctness,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
complexity,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Warn,
Complexity,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
perf,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Warn,
Perf,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
style,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Warn,
Style,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
suspicious,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Warn,
Suspicious,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
cargo,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Allow,
Cargo,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
nursery,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Allow,
Nursery,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
pedantic,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Allow,
Pedantic,
$($rest)*
}
};
(
$(#[$($meta:tt)*])*
$vis:vis $lint_name:ident,
restriction,
$($rest:tt)*
) => {
$crate::declare_clippy_lint_inner! {
$(#[$($meta)*])*
$vis $lint_name,
Allow,
Restriction,
$($rest)*
}
};
}

View file

@ -14,6 +14,7 @@ extern crate rustc_session;
extern crate rustc_span;
use clippy_utils::sym;
use declare_clippy_lint::LintListBuilder;
use rustc_interface::interface;
use rustc_session::EarlyDiagCtxt;
use rustc_session::config::ErrorOutputType;
@ -151,8 +152,13 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
(previous)(sess, lint_store);
}
let mut list_builder = LintListBuilder::default();
list_builder.insert(clippy_lints::declared_lints::LINTS);
list_builder.register(lint_store);
let conf = clippy_config::Conf::read(sess, &conf_path);
clippy_lints::register_lints(lint_store, conf);
clippy_lints::register_lint_passes(lint_store, conf);
#[cfg(feature = "internal")]
clippy_lints_internal::register_lints(lint_store);
}));

View file

@ -7,9 +7,9 @@ use askama::filters::Safe;
use cargo_metadata::Message;
use cargo_metadata::diagnostic::{Applicability, Diagnostic};
use clippy_config::ClippyConfiguration;
use clippy_lints::LintInfo;
use clippy_lints::declared_lints::LINTS;
use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
use declare_clippy_lint::LintInfo;
use pulldown_cmark::{Options, Parser, html};
use serde::Deserialize;
use test_utils::IS_RUSTC_TEST_SUITE;
@ -568,10 +568,10 @@ impl LintMetadata {
Self {
id: name,
id_location: Some(lint.location),
group: lint.category_str(),
group: lint.category.name(),
level: lint.lint.default_level.as_str(),
docs,
version: lint.version.unwrap(),
version: lint.version,
applicability,
}
}

View file

@ -40,6 +40,7 @@ fn dogfood() {
"clippy_lints",
"clippy_utils",
"clippy_config",
"declare_clippy_lint",
"lintcheck",
"rustc_tools_util",
] {

View file

@ -27,6 +27,7 @@ fn consistent_clippy_crate_versions() {
"clippy_config/Cargo.toml",
"clippy_lints/Cargo.toml",
"clippy_utils/Cargo.toml",
"declare_clippy_lint/Cargo.toml",
];
for path in paths {