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 const USING_FALLBACK_VERSION: &str = "UsingFallbackVersion";
22
23pub trait Rule: Send + Sync {
25 fn id(&self) -> &'static str;
30
31 fn description(&self) -> &'static str;
33
34 fn explanation(&self) -> &'static str;
36
37 fn deny(&mut self);
41
42 fn severity(&self) -> Severity;
44}
45
46pub fn rules() -> Vec<Box<dyn Rule>> {
48 let rules: Vec<Box<dyn Rule>> = vec![
49 Box::<UnusedImportRule>::default(),
50 Box::<UnusedInputRule>::default(),
51 Box::<UnusedDeclarationRule>::default(),
52 Box::<UnusedCallRule>::default(),
53 Box::<UnnecessaryFunctionCall>::default(),
54 Box::<UsingFallbackVersion>::default(),
55 ];
56
57 #[cfg(debug_assertions)]
59 {
60 use convert_case::Case;
61 use convert_case::Casing;
62 let mut set = std::collections::HashSet::new();
63 for r in rules.iter() {
64 if r.id().to_case(Case::Pascal) != r.id() {
65 panic!("analysis rule id `{id}` is not pascal case", id = r.id());
66 }
67
68 if !set.insert(r.id()) {
69 panic!("duplicate rule id `{id}`", id = r.id());
70 }
71 }
72 }
73
74 rules
75}
76
77#[derive(Debug, Clone, Copy)]
79pub struct UnusedImportRule(Severity);
80
81impl UnusedImportRule {
82 pub fn new() -> Self {
84 Self(Severity::Warning)
85 }
86}
87
88impl Default for UnusedImportRule {
89 fn default() -> Self {
90 Self::new()
91 }
92}
93
94impl Rule for UnusedImportRule {
95 fn id(&self) -> &'static str {
96 UNUSED_IMPORT_RULE_ID
97 }
98
99 fn description(&self) -> &'static str {
100 "Ensures that import namespaces are used in the importing document."
101 }
102
103 fn explanation(&self) -> &'static str {
104 "Imported WDL documents should be used in the document that imports them. Unused imports \
105 impact parsing and evaluation performance."
106 }
107
108 fn deny(&mut self) {
109 self.0 = Severity::Error;
110 }
111
112 fn severity(&self) -> Severity {
113 self.0
114 }
115}
116
117#[derive(Debug, Clone, Copy)]
119pub struct UnusedInputRule(Severity);
120
121impl UnusedInputRule {
122 pub fn new() -> Self {
124 Self(Severity::Warning)
125 }
126}
127
128impl Default for UnusedInputRule {
129 fn default() -> Self {
130 Self::new()
131 }
132}
133
134impl Rule for UnusedInputRule {
135 fn id(&self) -> &'static str {
136 UNUSED_INPUT_RULE_ID
137 }
138
139 fn description(&self) -> &'static str {
140 "Ensures that task or workspace inputs are used within the declaring task or workspace."
141 }
142
143 fn explanation(&self) -> &'static str {
144 "Unused inputs degrade evaluation performance and reduce the clarity of the code. Unused \
145 file inputs in tasks can also cause unnecessary file localizations."
146 }
147
148 fn deny(&mut self) {
149 self.0 = Severity::Error;
150 }
151
152 fn severity(&self) -> Severity {
153 self.0
154 }
155}
156
157#[derive(Debug, Clone, Copy)]
159pub struct UnusedDeclarationRule(Severity);
160
161impl UnusedDeclarationRule {
162 pub fn new() -> Self {
164 Self(Severity::Warning)
165 }
166}
167
168impl Default for UnusedDeclarationRule {
169 fn default() -> Self {
170 Self::new()
171 }
172}
173
174impl Rule for UnusedDeclarationRule {
175 fn id(&self) -> &'static str {
176 UNUSED_DECL_RULE_ID
177 }
178
179 fn description(&self) -> &'static str {
180 "Ensures that private declarations in tasks or workspaces are used within the declaring \
181 task or workspace."
182 }
183
184 fn explanation(&self) -> &'static str {
185 "Unused private declarations degrade evaluation performance and reduce the clarity of the \
186 code."
187 }
188
189 fn deny(&mut self) {
190 self.0 = Severity::Error;
191 }
192
193 fn severity(&self) -> Severity {
194 self.0
195 }
196}
197
198#[derive(Debug, Clone, Copy)]
200pub struct UnusedCallRule(Severity);
201
202impl UnusedCallRule {
203 pub fn new() -> Self {
205 Self(Severity::Warning)
206 }
207}
208
209impl Default for UnusedCallRule {
210 fn default() -> Self {
211 Self::new()
212 }
213}
214
215impl Rule for UnusedCallRule {
216 fn id(&self) -> &'static str {
217 UNUSED_CALL_RULE_ID
218 }
219
220 fn description(&self) -> &'static str {
221 "Ensures that outputs of a call statement are used in the declaring workflow."
222 }
223
224 fn explanation(&self) -> &'static str {
225 "Unused calls may cause unnecessary consumption of compute resources."
226 }
227
228 fn deny(&mut self) {
229 self.0 = Severity::Error;
230 }
231
232 fn severity(&self) -> Severity {
233 self.0
234 }
235}
236
237#[derive(Debug, Clone, Copy)]
239pub struct UnnecessaryFunctionCall(Severity);
240
241impl UnnecessaryFunctionCall {
242 pub fn new() -> Self {
244 Self(Severity::Warning)
245 }
246}
247
248impl Default for UnnecessaryFunctionCall {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254impl Rule for UnnecessaryFunctionCall {
255 fn id(&self) -> &'static str {
256 UNNECESSARY_FUNCTION_CALL
257 }
258
259 fn description(&self) -> &'static str {
260 "Ensures that function calls are necessary."
261 }
262
263 fn explanation(&self) -> &'static str {
264 "Unnecessary function calls may impact evaluation performance."
265 }
266
267 fn deny(&mut self) {
268 self.0 = Severity::Error;
269 }
270
271 fn severity(&self) -> Severity {
272 self.0
273 }
274}
275
276#[derive(Debug, Clone, Copy)]
278pub struct UsingFallbackVersion(Severity);
279
280impl UsingFallbackVersion {
281 pub fn new() -> Self {
283 Self(Severity::Warning)
284 }
285}
286
287impl Default for UsingFallbackVersion {
288 fn default() -> Self {
289 Self::new()
290 }
291}
292
293impl Rule for UsingFallbackVersion {
294 fn id(&self) -> &'static str {
295 USING_FALLBACK_VERSION
296 }
297
298 fn description(&self) -> &'static str {
299 "Warns if interpretation of a document with an unsupported version falls back to a default."
300 }
301
302 fn explanation(&self) -> &'static str {
303 "A document with an unsupported version may have unpredictable behavior if interpreted as \
304 a different version."
305 }
306
307 fn deny(&mut self) {
308 self.0 = Severity::Error;
309 }
310
311 fn severity(&self) -> Severity {
312 self.0
313 }
314}