forge_lint/sol/
macros.rs

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