1use wdl_ast::Severity;
4
5pub const UNUSED_IMPORT_RULE_ID: &str = "UnusedImport";
7
8pub const UNUSED_INPUT_RULE_ID: &str = "UnusedInput";
10
11pub const UNUSED_DECL_RULE_ID: &str = "UnusedDeclaration";
13
14pub const UNUSED_CALL_RULE_ID: &str = "UnusedCall";
16
17pub const UNNECESSARY_FUNCTION_CALL: &str = "UnnecessaryFunctionCall";
19
20pub trait Rule: Send + Sync {
22 fn id(&self) -> &'static str;
27
28 fn description(&self) -> &'static str;
30
31 fn explanation(&self) -> &'static str;
33
34 fn deny(&mut self);
38
39 fn severity(&self) -> Severity;
41}
42
43pub fn rules() -> Vec<Box<dyn Rule>> {
45 let rules: Vec<Box<dyn Rule>> = vec![
46 Box::<UnusedImportRule>::default(),
47 Box::<UnusedInputRule>::default(),
48 Box::<UnusedDeclarationRule>::default(),
49 Box::<UnusedCallRule>::default(),
50 Box::<UnnecessaryFunctionCall>::default(),
51 ];
52
53 #[cfg(debug_assertions)]
55 {
56 use convert_case::Case;
57 use convert_case::Casing;
58 let mut set = std::collections::HashSet::new();
59 for r in rules.iter() {
60 if r.id().to_case(Case::Pascal) != r.id() {
61 panic!("analysis rule id `{id}` is not pascal case", id = r.id());
62 }
63
64 if !set.insert(r.id()) {
65 panic!("duplicate rule id `{id}`", id = r.id());
66 }
67 }
68 }
69
70 rules
71}
72
73#[derive(Debug, Clone, Copy)]
75pub struct UnusedImportRule(Severity);
76
77impl UnusedImportRule {
78 pub fn new() -> Self {
80 Self(Severity::Warning)
81 }
82}
83
84impl Default for UnusedImportRule {
85 fn default() -> Self {
86 Self::new()
87 }
88}
89
90impl Rule for UnusedImportRule {
91 fn id(&self) -> &'static str {
92 UNUSED_IMPORT_RULE_ID
93 }
94
95 fn description(&self) -> &'static str {
96 "Ensures that import namespaces are used in the importing document."
97 }
98
99 fn explanation(&self) -> &'static str {
100 "Imported WDL documents should be used in the document that imports them. Unused imports \
101 impact parsing and evaluation performance."
102 }
103
104 fn deny(&mut self) {
105 self.0 = Severity::Error;
106 }
107
108 fn severity(&self) -> Severity {
109 self.0
110 }
111}
112
113#[derive(Debug, Clone, Copy)]
115pub struct UnusedInputRule(Severity);
116
117impl UnusedInputRule {
118 pub fn new() -> Self {
120 Self(Severity::Warning)
121 }
122}
123
124impl Default for UnusedInputRule {
125 fn default() -> Self {
126 Self::new()
127 }
128}
129
130impl Rule for UnusedInputRule {
131 fn id(&self) -> &'static str {
132 UNUSED_INPUT_RULE_ID
133 }
134
135 fn description(&self) -> &'static str {
136 "Ensures that task or workspace inputs are used within the declaring task or workspace."
137 }
138
139 fn explanation(&self) -> &'static str {
140 "Unused inputs degrade evaluation performance and reduce the clarity of the code. Unused \
141 file inputs in tasks can also cause unnecessary file localizations."
142 }
143
144 fn deny(&mut self) {
145 self.0 = Severity::Error;
146 }
147
148 fn severity(&self) -> Severity {
149 self.0
150 }
151}
152
153#[derive(Debug, Clone, Copy)]
155pub struct UnusedDeclarationRule(Severity);
156
157impl UnusedDeclarationRule {
158 pub fn new() -> Self {
160 Self(Severity::Warning)
161 }
162}
163
164impl Default for UnusedDeclarationRule {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl Rule for UnusedDeclarationRule {
171 fn id(&self) -> &'static str {
172 UNUSED_DECL_RULE_ID
173 }
174
175 fn description(&self) -> &'static str {
176 "Ensures that private declarations in tasks or workspaces are used within the declaring \
177 task or workspace."
178 }
179
180 fn explanation(&self) -> &'static str {
181 "Unused private declarations degrade evaluation performance and reduce the clarity of the \
182 code."
183 }
184
185 fn deny(&mut self) {
186 self.0 = Severity::Error;
187 }
188
189 fn severity(&self) -> Severity {
190 self.0
191 }
192}
193
194#[derive(Debug, Clone, Copy)]
196pub struct UnusedCallRule(Severity);
197
198impl UnusedCallRule {
199 pub fn new() -> Self {
201 Self(Severity::Warning)
202 }
203}
204
205impl Default for UnusedCallRule {
206 fn default() -> Self {
207 Self::new()
208 }
209}
210
211impl Rule for UnusedCallRule {
212 fn id(&self) -> &'static str {
213 UNUSED_CALL_RULE_ID
214 }
215
216 fn description(&self) -> &'static str {
217 "Ensures that outputs of a call statement are used in the declaring workflow."
218 }
219
220 fn explanation(&self) -> &'static str {
221 "Unused calls may cause unnecessary consumption of compute resources."
222 }
223
224 fn deny(&mut self) {
225 self.0 = Severity::Error;
226 }
227
228 fn severity(&self) -> Severity {
229 self.0
230 }
231}
232
233#[derive(Debug, Clone, Copy)]
235pub struct UnnecessaryFunctionCall(Severity);
236
237impl UnnecessaryFunctionCall {
238 pub fn new() -> Self {
240 Self(Severity::Warning)
241 }
242}
243
244impl Default for UnnecessaryFunctionCall {
245 fn default() -> Self {
246 Self::new()
247 }
248}
249
250impl Rule for UnnecessaryFunctionCall {
251 fn id(&self) -> &'static str {
252 UNNECESSARY_FUNCTION_CALL
253 }
254
255 fn description(&self) -> &'static str {
256 "Ensures that function calls are necessary."
257 }
258
259 fn explanation(&self) -> &'static str {
260 "Unnecessary function calls may impact evaluation performance."
261 }
262
263 fn deny(&mut self) {
264 self.0 = Severity::Error;
265 }
266
267 fn severity(&self) -> Severity {
268 self.0
269 }
270}