forge_lint/linter/
early.rs

1use solar_ast::{self as ast, visit::Visit};
2use solar_interface::data_structures::Never;
3use std::ops::ControlFlow;
4
5use super::LintContext;
6
7/// Trait for lints that operate directly on the AST.
8/// Its methods mirror `ast::visit::Visit`, with the addition of `LintCotext`.
9pub trait EarlyLintPass<'ast>: Send + Sync {
10    fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast ast::Expr<'ast>) {}
11    fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ast::ItemStruct<'ast>) {}
12    fn check_item_function(
13        &mut self,
14        _ctx: &LintContext<'_>,
15        _func: &'ast ast::ItemFunction<'ast>,
16    ) {
17    }
18    fn check_variable_definition(
19        &mut self,
20        _ctx: &LintContext<'_>,
21        _var: &'ast ast::VariableDefinition<'ast>,
22    ) {
23    }
24    fn check_import_directive(
25        &mut self,
26        _ctx: &LintContext<'_>,
27        _import: &'ast ast::ImportDirective<'ast>,
28    ) {
29    }
30    fn check_using_directive(
31        &mut self,
32        _ctx: &LintContext<'_>,
33        _using: &'ast ast::UsingDirective<'ast>,
34    ) {
35    }
36    fn check_item_contract(
37        &mut self,
38        _ctx: &LintContext<'_>,
39        _contract: &'ast ast::ItemContract<'ast>,
40    ) {
41    }
42    fn check_doc_comment(&mut self, _ctx: &LintContext<'_>, _cmnt: &'ast ast::DocComment) {}
43    // TODO: Add methods for each required AST node type
44
45    /// Should be called after the source unit has been visited. Enables lints that require
46    /// knowledge of the entire AST to perform their analysis.
47    fn check_full_source_unit(
48        &mut self,
49        _ctx: &LintContext<'ast>,
50        _ast: &'ast ast::SourceUnit<'ast>,
51    ) {
52    }
53}
54
55/// Visitor struct for `EarlyLintPass`es
56pub struct EarlyLintVisitor<'a, 's, 'ast> {
57    pub ctx: &'a LintContext<'s>,
58    pub passes: &'a mut [Box<dyn EarlyLintPass<'ast> + 's>],
59}
60
61impl<'a, 's, 'ast> EarlyLintVisitor<'a, 's, 'ast>
62where
63    's: 'ast,
64{
65    pub fn new(
66        ctx: &'a LintContext<'s>,
67        passes: &'a mut [Box<dyn EarlyLintPass<'ast> + 's>],
68    ) -> Self {
69        Self { ctx, passes }
70    }
71
72    /// Extends the [`Visit`] trait functionality with a hook that can run after the initial
73    /// traversal.
74    pub fn post_source_unit(&mut self, ast: &'ast ast::SourceUnit<'ast>) {
75        for pass in self.passes.iter_mut() {
76            pass.check_full_source_unit(self.ctx, ast);
77        }
78    }
79}
80
81impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast>
82where
83    's: 'ast,
84{
85    type BreakValue = Never;
86
87    fn visit_doc_comment(&mut self, cmnt: &'ast ast::DocComment) -> ControlFlow<Self::BreakValue> {
88        for pass in self.passes.iter_mut() {
89            pass.check_doc_comment(self.ctx, cmnt)
90        }
91        self.walk_doc_comment(cmnt)
92    }
93
94    fn visit_expr(&mut self, expr: &'ast ast::Expr<'ast>) -> ControlFlow<Self::BreakValue> {
95        for pass in self.passes.iter_mut() {
96            pass.check_expr(self.ctx, expr)
97        }
98        self.walk_expr(expr)
99    }
100
101    fn visit_variable_definition(
102        &mut self,
103        var: &'ast ast::VariableDefinition<'ast>,
104    ) -> ControlFlow<Self::BreakValue> {
105        for pass in self.passes.iter_mut() {
106            pass.check_variable_definition(self.ctx, var)
107        }
108        self.walk_variable_definition(var)
109    }
110
111    fn visit_item_struct(
112        &mut self,
113        strukt: &'ast ast::ItemStruct<'ast>,
114    ) -> ControlFlow<Self::BreakValue> {
115        for pass in self.passes.iter_mut() {
116            pass.check_item_struct(self.ctx, strukt)
117        }
118        self.walk_item_struct(strukt)
119    }
120
121    fn visit_item_function(
122        &mut self,
123        func: &'ast ast::ItemFunction<'ast>,
124    ) -> ControlFlow<Self::BreakValue> {
125        for pass in self.passes.iter_mut() {
126            pass.check_item_function(self.ctx, func)
127        }
128        self.walk_item_function(func)
129    }
130
131    fn visit_import_directive(
132        &mut self,
133        import: &'ast ast::ImportDirective<'ast>,
134    ) -> ControlFlow<Self::BreakValue> {
135        for pass in self.passes.iter_mut() {
136            pass.check_import_directive(self.ctx, import);
137        }
138        self.walk_import_directive(import)
139    }
140
141    fn visit_using_directive(
142        &mut self,
143        using: &'ast ast::UsingDirective<'ast>,
144    ) -> ControlFlow<Self::BreakValue> {
145        for pass in self.passes.iter_mut() {
146            pass.check_using_directive(self.ctx, using);
147        }
148        self.walk_using_directive(using)
149    }
150
151    fn visit_item_contract(
152        &mut self,
153        contract: &'ast ast::ItemContract<'ast>,
154    ) -> ControlFlow<Self::BreakValue> {
155        for pass in self.passes.iter_mut() {
156            pass.check_item_contract(self.ctx, contract);
157        }
158        self.walk_item_contract(contract)
159    }
160
161    // TODO: Add methods for each required AST node type, mirroring `solar_ast::visit::Visit` method
162    // sigs + adding `LintContext`
163}