Turn declare_clippy_lint into a declarative macro
This commit is contained in:
parent
43e3384581
commit
4997ee7afc
|
@ -13,7 +13,6 @@ 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"
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
#[macro_export]
|
||||
#[allow(clippy::crate_in_macro_def)]
|
||||
macro_rules! declare_clippy_lint {
|
||||
(@
|
||||
$(#[doc = $lit:literal])*
|
||||
pub $lint_name:ident,
|
||||
$category:ident,
|
||||
$lintcategory:expr,
|
||||
$desc:literal,
|
||||
$version_expr:expr,
|
||||
$version_lit:literal
|
||||
) => {
|
||||
rustc_session::declare_tool_lint! {
|
||||
$(#[doc = $lit])*
|
||||
#[clippy::version = $version_lit]
|
||||
pub clippy::$lint_name,
|
||||
$category,
|
||||
$desc,
|
||||
report_in_external_macro:true
|
||||
}
|
||||
|
||||
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
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
style,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Style, $desc,
|
||||
Some($version), $version
|
||||
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
correctness,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
|
||||
Some($version), $version
|
||||
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
perf,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
complexity,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
suspicious,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
nursery,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
pedantic,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
#[clippy::version = $version:literal]
|
||||
pub $lint_name:ident,
|
||||
cargo,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
|
||||
Some($version), $version
|
||||
}
|
||||
};
|
||||
|
||||
(
|
||||
$(#[doc = $lit:literal])*
|
||||
pub $lint_name:ident,
|
||||
internal,
|
||||
$desc:literal
|
||||
) => {
|
||||
declare_clippy_lint! {@
|
||||
$(#[doc = $lit])*
|
||||
pub $lint_name, Allow, crate::LintCategory::Internal, $desc,
|
||||
None, "0.0.0"
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(array_windows)]
|
||||
#![feature(binary_heap_into_iter_sorted)]
|
||||
#![feature(box_patterns)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(control_flow_enum)]
|
||||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
|
@ -59,9 +60,10 @@ extern crate rustc_trait_selection;
|
|||
extern crate thin_vec;
|
||||
|
||||
#[macro_use]
|
||||
extern crate clippy_utils;
|
||||
mod declare_clippy_lint;
|
||||
|
||||
#[macro_use]
|
||||
extern crate declare_clippy_lint;
|
||||
extern crate clippy_utils;
|
||||
|
||||
#[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
|
||||
mod utils;
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#![feature(f128)]
|
||||
#![feature(f16)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(macro_metavar_expr_concat)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(never_type)]
|
||||
#![feature(rustc_private)]
|
||||
|
@ -129,7 +130,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
|
|||
use crate::higher::Range;
|
||||
use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
|
||||
use crate::visitors::for_each_expr_without_closures;
|
||||
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
||||
#[macro_export]
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
[package]
|
||||
name = "declare_clippy_lint"
|
||||
version = "0.1.83"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
itertools = "0.12"
|
||||
quote = "1.0.21"
|
||||
syn = "2.0"
|
|
@ -1,182 +0,0 @@
|
|||
#![feature(let_chains, proc_macro_span)]
|
||||
// warn on lints, that are included in `rust-lang/rust`s bootstrap
|
||||
#![warn(rust_2018_idioms, unused_lifetimes)]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::{format_ident, quote};
|
||||
use syn::parse::{Parse, ParseStream};
|
||||
use syn::{Attribute, Error, Expr, ExprLit, Ident, Lit, LitStr, Meta, Result, Token, parse_macro_input};
|
||||
|
||||
fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) -> Option<LitStr> {
|
||||
if let Meta::NameValue(name_value) = &attr.meta {
|
||||
let path_idents = name_value.path.segments.iter().map(|segment| &segment.ident);
|
||||
|
||||
if itertools::equal(path_idents, path)
|
||||
&& let Expr::Lit(ExprLit { lit: Lit::Str(s), .. }) = &name_value.value
|
||||
{
|
||||
return Some(s.clone());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
struct ClippyLint {
|
||||
attrs: Vec<Attribute>,
|
||||
version: Option<LitStr>,
|
||||
explanation: String,
|
||||
name: Ident,
|
||||
category: Ident,
|
||||
description: LitStr,
|
||||
}
|
||||
|
||||
impl Parse for ClippyLint {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
let attrs = input.call(Attribute::parse_outer)?;
|
||||
|
||||
let mut in_code = false;
|
||||
let mut explanation = String::new();
|
||||
let mut version = None;
|
||||
for attr in &attrs {
|
||||
if let Some(lit) = parse_attr(["doc"], attr) {
|
||||
let value = lit.value();
|
||||
let line = value.strip_prefix(' ').unwrap_or(&value);
|
||||
|
||||
if let Some(lang) = line.strip_prefix("```") {
|
||||
let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
|
||||
if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
|
||||
explanation += "```rust\n";
|
||||
} else {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
}
|
||||
in_code = !in_code;
|
||||
} else if !(in_code && line.starts_with("# ")) {
|
||||
explanation += line;
|
||||
explanation.push('\n');
|
||||
}
|
||||
} else if let Some(lit) = parse_attr(["clippy", "version"], attr) {
|
||||
if let Some(duplicate) = version.replace(lit) {
|
||||
return Err(Error::new_spanned(duplicate, "duplicate clippy::version"));
|
||||
}
|
||||
} else {
|
||||
return Err(Error::new_spanned(attr, "unexpected attribute"));
|
||||
}
|
||||
}
|
||||
|
||||
input.parse::<Token![pub]>()?;
|
||||
let name = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
let category = input.parse()?;
|
||||
input.parse::<Token![,]>()?;
|
||||
|
||||
let description = input.parse()?;
|
||||
|
||||
Ok(Self {
|
||||
attrs,
|
||||
version,
|
||||
explanation,
|
||||
name,
|
||||
category,
|
||||
description,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Macro used to declare a Clippy lint.
|
||||
///
|
||||
/// Every lint declaration consists of 4 parts:
|
||||
///
|
||||
/// 1. The documentation, which is used for the website and `cargo clippy --explain`
|
||||
/// 2. The `LINT_NAME`. See [lint naming][lint_naming] on lint naming conventions.
|
||||
/// 3. The `lint_level`, which is a mapping from *one* of our lint groups to `Allow`, `Warn` or
|
||||
/// `Deny`. The lint level here has nothing to do with what lint groups the lint is a part of.
|
||||
/// 4. The `description` that contains a short explanation on what's wrong with code where the lint
|
||||
/// is triggered.
|
||||
///
|
||||
/// Currently the categories `style`, `correctness`, `suspicious`, `complexity` and `perf` are
|
||||
/// enabled by default. As said in the README.md of this repository, if the lint level mapping
|
||||
/// changes, please update README.md.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// use rustc_session::declare_tool_lint;
|
||||
///
|
||||
/// declare_clippy_lint! {
|
||||
/// /// ### What it does
|
||||
/// /// Checks for ... (describe what the lint matches).
|
||||
/// ///
|
||||
/// /// ### Why is this bad?
|
||||
/// /// Supply the reason for linting the code.
|
||||
/// ///
|
||||
/// /// ### Example
|
||||
/// /// ```rust
|
||||
/// /// Insert a short example of code that triggers the lint
|
||||
/// /// ```
|
||||
/// ///
|
||||
/// /// Use instead:
|
||||
/// /// ```rust
|
||||
/// /// Insert a short example of improved code that doesn't trigger the lint
|
||||
/// /// ```
|
||||
/// #[clippy::version = "1.65.0"]
|
||||
/// pub LINT_NAME,
|
||||
/// pedantic,
|
||||
/// "description"
|
||||
/// }
|
||||
/// ```
|
||||
/// [lint_naming]: https://rust-lang.github.io/rfcs/0344-conventions-galore.html#lints
|
||||
#[proc_macro]
|
||||
pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
|
||||
let ClippyLint {
|
||||
attrs,
|
||||
version,
|
||||
explanation,
|
||||
name,
|
||||
category,
|
||||
description,
|
||||
} = parse_macro_input!(input as ClippyLint);
|
||||
|
||||
let mut category = category.to_string();
|
||||
|
||||
let level = format_ident!("{}", match category.as_str() {
|
||||
"correctness" => "Deny",
|
||||
"style" | "suspicious" | "complexity" | "perf" => "Warn",
|
||||
"pedantic" | "restriction" | "cargo" | "nursery" | "internal" => "Allow",
|
||||
_ => panic!("unknown category {category}"),
|
||||
},);
|
||||
|
||||
let info_name = format_ident!("{name}_INFO");
|
||||
|
||||
(&mut category[0..1]).make_ascii_uppercase();
|
||||
let category_variant = format_ident!("{category}");
|
||||
|
||||
let name_span = name.span().unwrap();
|
||||
let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line());
|
||||
|
||||
let version = match version {
|
||||
Some(version) => quote!(Some(#version)),
|
||||
None => quote!(None),
|
||||
};
|
||||
|
||||
let output = quote! {
|
||||
rustc_session::declare_tool_lint! {
|
||||
#(#attrs)*
|
||||
pub clippy::#name,
|
||||
#level,
|
||||
#description,
|
||||
report_in_external_macro: true
|
||||
}
|
||||
|
||||
pub(crate) static #info_name: &'static crate::LintInfo = &crate::LintInfo {
|
||||
lint: &#name,
|
||||
category: crate::LintCategory::#category_variant,
|
||||
explanation: #explanation,
|
||||
location: #location,
|
||||
version: #version,
|
||||
};
|
||||
};
|
||||
|
||||
TokenStream::from(output)
|
||||
}
|
|
@ -24,7 +24,6 @@ fn consistent_clippy_crate_versions() {
|
|||
let clippy_version = read_version("Cargo.toml");
|
||||
|
||||
let paths = [
|
||||
"declare_clippy_lint/Cargo.toml",
|
||||
"clippy_config/Cargo.toml",
|
||||
"clippy_lints/Cargo.toml",
|
||||
"clippy_utils/Cargo.toml",
|
||||
|
|
Loading…
Reference in New Issue