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    ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => {
28        $crate::declare_forge_lint!($id, $severity, $str_id, $desc, "");
29    };
30}
31
32/// Registers Solidity linter passes that can have both early and late variants.
33///
34/// # Parameters
35///
36/// Each pass is declared with:
37/// - `$pass_id`: Identifier of the generated struct that will implement the pass trait(s).
38/// - `$pass_type`: Either `early`, `late`, or `both` to indicate which traits to implement.
39/// - `$lints`: A parenthesized, comma-separated list of `SolLint` constants.
40///
41/// # Outputs
42///
43/// - Structs for each linting pass
44/// - Helper methods to create early and late passes with required lifetimes
45/// - `const REGISTERED_LINTS` containing all registered lint objects
46#[macro_export]
47macro_rules! register_lints {
48    // 1. Internal rule for declaring structs and their associated lints.
49    ( @declare_structs $( ($pass_id:ident, $pass_type:ident, ($($lint:expr),* $(,)?)) ),* $(,)? ) => {
50        $(
51            #[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
52            pub struct $pass_id;
53
54            impl $pass_id {
55                /// Static slice of lints associated with this pass.
56                const LINTS: &'static [SolLint] = &[$($lint),*];
57
58                register_lints!(@early_impl $pass_id, $pass_type);
59                register_lints!(@late_impl $pass_id, $pass_type);
60            }
61        )*
62    };
63
64    // 2. Internal rule for declaring the const array of ALL lints.
65    ( @declare_consts $( ($pass_id:ident, $pass_type:ident, ($($lint:expr),* $(,)?)) ),* $(,)? ) => {
66        pub const REGISTERED_LINTS: &[SolLint] = &[
67            $(
68                $($lint,)*
69            )*
70        ];
71    };
72
73    // 3. Internal rule for declaring the helper functions.
74    ( @declare_funcs $( ($pass_id:ident, $pass_type:ident, $lints:tt) ),* $(,)? ) => {
75        pub fn create_early_lint_passes<'ast>() -> Vec<(Box<dyn EarlyLintPass<'ast>>, &'static [SolLint])> {
76            [
77                $(
78                    register_lints!(@early_create $pass_id, $pass_type),
79                )*
80            ]
81            .into_iter()
82            .flatten()
83            .collect()
84        }
85
86        pub fn create_late_lint_passes<'hir>() -> Vec<(Box<dyn LateLintPass<'hir>>, &'static [SolLint])> {
87            [
88                $(
89                    register_lints!(@late_create $pass_id, $pass_type),
90                )*
91            ]
92            .into_iter()
93            .flatten()
94            .collect()
95        }
96    };
97
98    // --- HELPERS ------------------------------------------------------------
99    (@early_impl $_pass_id:ident, late) => {};
100    (@early_impl $pass_id:ident, $other:ident) => {
101        pub fn as_early_lint_pass<'a>() -> Box<dyn EarlyLintPass<'a>> {
102            Box::new(Self::default())
103        }
104    };
105
106    (@late_impl $_pass_id:ident, early) => {};
107    (@late_impl $pass_id:ident, $other:ident) => {
108        pub fn as_late_lint_pass<'hir>() -> Box<dyn LateLintPass<'hir>> {
109            Box::new(Self::default())
110        }
111    };
112
113    (@early_create $_pass_id:ident, late) => { None };
114    (@early_create $pass_id:ident, $_other:ident) => {
115        Some(($pass_id::as_early_lint_pass(), $pass_id::LINTS))
116    };
117
118    (@late_create $_pass_id:ident, early) => { None };
119    (@late_create $pass_id:ident, $_other:ident) => {
120        Some(($pass_id::as_late_lint_pass(), $pass_id::LINTS))
121    };
122
123    // --- ENTRY POINT ---------------------------------------------------------
124    ( $($tokens:tt)* ) => {
125        register_lints! { @declare_structs $($tokens)* }
126        register_lints! { @declare_consts  $($tokens)* }
127        register_lints! { @declare_funcs   $($tokens)* }
128    };
129}