Skip to main content

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 corresponding markdown documentation file at
13/// `crates/lint/docs/<str_id>.md`. The `help` URL is auto-generated by the macro and points to
14/// the per-lint page on the Foundry docs site (`getfoundry.sh/forge/linting/<str_id>`). To
15/// ensure that new lint rules have their corresponding docs, the existence of every registered
16/// lint's markdown file is validated by a unit test (see `crates/lint/src/sol/mod.rs`).
17#[macro_export]
18macro_rules! declare_forge_lint {
19    ($id:ident, $severity:expr, $str_id:expr, $desc:expr) => {
20        // Declare the static `Lint` metadata
21        pub static $id: SolLint = SolLint {
22            id: $str_id,
23            severity: $severity,
24            description: $desc,
25            help: concat!("https://getfoundry.sh/forge/linting/", $str_id),
26        };
27    };
28}
29
30/// Registers Solidity linter passes that can have both early and late variants.
31///
32/// # Parameters
33///
34/// Each pass is declared with:
35/// - `$pass_id`: Identifier of the generated struct that will implement the pass trait(s).
36/// - `$pass_type`: `early`, `late`, `both`, or `project`.
37/// - `$lints`: A parenthesized, comma-separated list of `SolLint` constants.
38/// - an optional constructor that receives the lint-specific configuration.
39///
40/// # Outputs
41///
42/// - Marker structs for each linting pass.
43/// - `const REGISTERED_LINTS` containing all registered lint objects.
44/// - A function that adds fresh pass factories to Solar's lint registry.
45#[macro_export]
46macro_rules! register_lints {
47    // 1. Internal rule for declaring structs and their associated lints.
48    ( @declare_structs $( ($pass_id:ident, $pass_type:ident, ($($lint:expr),* $(,)?) $(, $constructor:expr)? ) ),* $(,)? ) => {
49        $(
50            #[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
51            pub struct $pass_id;
52
53            impl $pass_id {
54                /// Static slice of lint identifiers associated with this pass.
55                const LINT_IDS: &'static [&'static str] = &[$($lint.id),*];
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),* $(,)?) $(, $constructor: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 $(, $constructor:expr)?) ),* $(,)? ) => {
71        pub fn register_lints(
72            registry: &mut solar_lint::LintRegistry,
73            config: &std::sync::Arc<foundry_config::lint::LintSpecificConfig>,
74        ) {
75            let _ = config;
76            $(
77                register_lints!(@register_early registry, config, $pass_id, $pass_type $(, $constructor)?);
78                register_lints!(@register_late registry, config, $pass_id, $pass_type $(, $constructor)?);
79                register_lints!(@register_project registry, config, $pass_id, $pass_type $(, $constructor)?);
80            )*
81        }
82    };
83
84    // --- HELPERS ------------------------------------------------------------
85    (@register_early $registry:ident, $config:ident, $_pass_id:ident, late $(, $constructor:expr)?) => {};
86    (@register_early $registry:ident, $config:ident, $_pass_id:ident, project $(, $constructor:expr)?) => {};
87    (@register_early $registry:ident, $config:ident, $pass_id:ident, $_other:ident, $constructor:expr) => {{
88        let config = std::sync::Arc::clone($config);
89        $registry.register_early_pass(
90            $pass_id::LINT_IDS,
91            move || ($constructor)(std::sync::Arc::clone(&config)),
92        );
93    }};
94    (@register_early $registry:ident, $config:ident, $pass_id:ident, $_other:ident) => {
95        $registry.register_early_pass($pass_id::LINT_IDS, $pass_id::default);
96    };
97
98    (@register_late $registry:ident, $config:ident, $_pass_id:ident, early $(, $constructor:expr)?) => {};
99    (@register_late $registry:ident, $config:ident, $_pass_id:ident, project $(, $constructor:expr)?) => {};
100    (@register_late $registry:ident, $config:ident, $pass_id:ident, $_other:ident, $constructor:expr) => {{
101        let config = std::sync::Arc::clone($config);
102        $registry.register_late_pass(
103            $pass_id::LINT_IDS,
104            move || ($constructor)(std::sync::Arc::clone(&config)),
105        );
106    }};
107    (@register_late $registry:ident, $config:ident, $pass_id:ident, $_other:ident) => {
108        $registry.register_late_pass($pass_id::LINT_IDS, $pass_id::default);
109    };
110
111    (@register_project $registry:ident, $config:ident, $_pass_id:ident, early $(, $constructor:expr)?) => {};
112    (@register_project $registry:ident, $config:ident, $_pass_id:ident, late $(, $constructor:expr)?) => {};
113    (@register_project $registry:ident, $config:ident, $_pass_id:ident, both $(, $constructor:expr)?) => {};
114    (@register_project $registry:ident, $config:ident, $pass_id:ident, $_other:ident, $constructor:expr) => {{
115        let config = std::sync::Arc::clone($config);
116        $registry.register_project_pass(
117            $pass_id::LINT_IDS,
118            move || ($constructor)(std::sync::Arc::clone(&config)),
119        );
120    }};
121    (@register_project $registry:ident, $config:ident, $pass_id:ident, $_other:ident) => {
122        $registry.register_project_pass($pass_id::LINT_IDS, $pass_id::default);
123    };
124
125    // --- ENTRY POINT ---------------------------------------------------------
126    ( $($tokens:tt)* ) => {
127        register_lints! { @declare_structs $($tokens)* }
128        register_lints! { @declare_consts  $($tokens)* }
129        register_lints! { @declare_funcs   $($tokens)* }
130    };
131}