forge_lint/
linter.rs
1use foundry_compilers::Language;
2use foundry_config::lint::Severity;
3use solar_ast::{visit::Visit, Expr, ItemFunction, ItemStruct, VariableDefinition};
4use solar_interface::{
5 data_structures::Never,
6 diagnostics::{DiagBuilder, DiagId, MultiSpan},
7 Session, Span,
8};
9use std::{ops::ControlFlow, path::PathBuf};
10
11pub trait Linter: Send + Sync + Clone {
24 type Language: Language;
25 type Lint: Lint;
26
27 fn lint(&self, input: &[PathBuf]);
28}
29
30pub trait Lint {
31 fn id(&self) -> &'static str;
32 fn severity(&self) -> Severity;
33 fn description(&self) -> &'static str;
34 fn help(&self) -> &'static str;
35}
36
37pub struct LintContext<'s> {
38 sess: &'s Session,
39 desc: bool,
40}
41
42impl<'s> LintContext<'s> {
43 pub fn new(sess: &'s Session, with_description: bool) -> Self {
44 Self { sess, desc: with_description }
45 }
46
47 pub fn emit<L: Lint>(&self, lint: &'static L, span: Span) {
49 let desc = if self.desc { lint.description() } else { "" };
50 let diag: DiagBuilder<'_, ()> = self
51 .sess
52 .dcx
53 .diag(lint.severity().into(), desc)
54 .code(DiagId::new_str(lint.id()))
55 .span(MultiSpan::from_span(span))
56 .help(lint.help());
57
58 diag.emit();
59 }
60}
61
62pub trait EarlyLintPass<'ast>: Send + Sync {
65 fn check_expr(&mut self, _ctx: &LintContext<'_>, _expr: &'ast Expr<'ast>) {}
66 fn check_item_struct(&mut self, _ctx: &LintContext<'_>, _struct: &'ast ItemStruct<'ast>) {}
67 fn check_item_function(&mut self, _ctx: &LintContext<'_>, _func: &'ast ItemFunction<'ast>) {}
68 fn check_variable_definition(
69 &mut self,
70 _ctx: &LintContext<'_>,
71 _var: &'ast VariableDefinition<'ast>,
72 ) {
73 }
74
75 }
77
78pub struct EarlyLintVisitor<'a, 's, 'ast> {
80 pub ctx: &'a LintContext<'s>,
81 pub passes: &'a mut [Box<dyn EarlyLintPass<'ast> + 's>],
82}
83
84impl<'s, 'ast> Visit<'ast> for EarlyLintVisitor<'_, 's, 'ast>
85where
86 's: 'ast,
87{
88 type BreakValue = Never;
89
90 fn visit_expr(&mut self, expr: &'ast Expr<'ast>) -> ControlFlow<Self::BreakValue> {
91 for pass in self.passes.iter_mut() {
92 pass.check_expr(self.ctx, expr)
93 }
94 self.walk_expr(expr)
95 }
96
97 fn visit_variable_definition(
98 &mut self,
99 var: &'ast VariableDefinition<'ast>,
100 ) -> ControlFlow<Self::BreakValue> {
101 for pass in self.passes.iter_mut() {
102 pass.check_variable_definition(self.ctx, var)
103 }
104 self.walk_variable_definition(var)
105 }
106
107 fn visit_item_struct(
108 &mut self,
109 strukt: &'ast ItemStruct<'ast>,
110 ) -> ControlFlow<Self::BreakValue> {
111 for pass in self.passes.iter_mut() {
112 pass.check_item_struct(self.ctx, strukt)
113 }
114 self.walk_item_struct(strukt)
115 }
116
117 fn visit_item_function(
118 &mut self,
119 func: &'ast ItemFunction<'ast>,
120 ) -> ControlFlow<Self::BreakValue> {
121 for pass in self.passes.iter_mut() {
122 pass.check_item_function(self.ctx, func)
123 }
124 self.walk_item_function(func)
125 }
126
127 }