forge_lint/sol/macros.rs
1/// Macro for defining lints and relevant metadata for the Solidity linter.
2///
3/// # Parameters
4///
5/// Each lint requires the following input fields:
6/// - `$id`: Identifier of the generated `SolLint` constant.
7/// - `$severity`: The `Severity` of the lint (e.g. `High`, `Med`, `Low`, `Info`, `Gas`).
8/// - `$str_id`: A unique identifier used to reference a specific lint during configuration.
9/// - `$desc`: A short description of the lint.
10///
11/// # Note
12/// Each lint must have a `help` section in the foundry book. This help field is auto-generated by
13/// the macro. Because of that, to ensure that new lint rules have their corresponding docs in the
14/// book, the existence of the lint rule's help section is validated with a unit test.
15#[macro_export]
16macro_rules! declare_forge_lint {
17 ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => {
18 // Declare the static `Lint` metadata
19 pub static $id: SolLint = SolLint {
20 id: $str_id,
21 severity: $severity,
22 description: $desc,
23 help: concat!("https://book.getfoundry.sh/reference/forge/forge-lint#", $str_id),
24 };
25 };
26}
27
28/// Registers Solidity linter passes that can have both early and late variants.
29///
30/// # Parameters
31///
32/// Each pass is declared with:
33/// - `$pass_id`: Identifier of the generated struct that will implement the pass trait(s).
34/// - `$pass_type`: Either `early`, `late`, or `both` to indicate which traits to implement.
35/// - `$lints`: A parenthesized, comma-separated list of `SolLint` constants.
36///
37/// # Outputs
38///
39/// - Structs for each linting pass
40/// - Helper methods to create early and late passes with required lifetimes
41/// - `const REGISTERED_LINTS` containing all registered lint objects
42#[macro_export]
43macro_rules! register_lints {
44 // 1. Internal rule for declaring structs and their associated lints.
45 ( @declare_structs $( ($pass_id:ident, $pass_type:ident, ($($lint:expr),* $(,)?)) ),* $(,)? ) => {
46 $(
47 #[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
48 pub struct $pass_id;
49
50 impl $pass_id {
51 /// Static slice of lints associated with this pass.
52 const LINTS: &'static [SolLint] = &[$($lint),*];
53
54 register_lints!(@early_impl $pass_id, $pass_type);
55 register_lints!(@late_impl $pass_id, $pass_type);
56 }
57 )*
58 };
59
60 // 2. Internal rule for declaring the const array of ALL lints.
61 ( @declare_consts $( ($pass_id:ident, $pass_type:ident, ($($lint:expr),* $(,)?)) ),* $(,)? ) => {
62 pub const REGISTERED_LINTS: &[SolLint] = &[
63 $(
64 $($lint,)*
65 )*
66 ];
67 };
68
69 // 3. Internal rule for declaring the helper functions.
70 ( @declare_funcs $( ($pass_id:ident, $pass_type:ident, $lints:tt) ),* $(,)? ) => {
71 pub fn create_early_lint_passes<'ast>() -> Vec<(Box<dyn EarlyLintPass<'ast>>, &'static [SolLint])> {
72 [
73 $(
74 register_lints!(@early_create $pass_id, $pass_type),
75 )*
76 ]
77 .into_iter()
78 .flatten()
79 .collect()
80 }
81
82 pub fn create_late_lint_passes<'hir>() -> Vec<(Box<dyn LateLintPass<'hir>>, &'static [SolLint])> {
83 [
84 $(
85 register_lints!(@late_create $pass_id, $pass_type),
86 )*
87 ]
88 .into_iter()
89 .flatten()
90 .collect()
91 }
92 };
93
94 // --- HELPERS ------------------------------------------------------------
95 (@early_impl $_pass_id:ident, late) => {};
96 (@early_impl $pass_id:ident, $other:ident) => {
97 pub fn as_early_lint_pass<'a>() -> Box<dyn EarlyLintPass<'a>> {
98 Box::new(Self::default())
99 }
100 };
101
102 (@late_impl $_pass_id:ident, early) => {};
103 (@late_impl $pass_id:ident, $other:ident) => {
104 pub fn as_late_lint_pass<'hir>() -> Box<dyn LateLintPass<'hir>> {
105 Box::new(Self::default())
106 }
107 };
108
109 (@early_create $_pass_id:ident, late) => { None };
110 (@early_create $pass_id:ident, $_other:ident) => {
111 Some(($pass_id::as_early_lint_pass(), $pass_id::LINTS))
112 };
113
114 (@late_create $_pass_id:ident, early) => { None };
115 (@late_create $pass_id:ident, $_other:ident) => {
116 Some(($pass_id::as_late_lint_pass(), $pass_id::LINTS))
117 };
118
119 // --- ENTRY POINT ---------------------------------------------------------
120 ( $($tokens:tt)* ) => {
121 register_lints! { @declare_structs $($tokens)* }
122 register_lints! { @declare_consts $($tokens)* }
123 register_lints! { @declare_funcs $($tokens)* }
124 };
125}