1#![doc = include_str!("../RULES.md")]
3#![doc = include_str!("../DEFINITIONS.md")]
5#![warn(missing_docs)]
20#![warn(rust_2018_idioms)]
21#![warn(rust_2021_compatibility)]
22#![warn(missing_debug_implementations)]
23#![warn(clippy::missing_docs_in_private_items)]
24#![warn(rustdoc::broken_intra_doc_links)]
25
26use wdl_analysis::Visitor;
27use wdl_ast::SyntaxKind;
28
29pub(crate) mod fix;
30mod linter;
31pub mod rules;
32mod tags;
33pub(crate) mod util;
34
35pub use linter::*;
36pub use tags::*;
37pub use util::find_nearest_rule;
38pub use wdl_analysis as analysis;
39pub use wdl_ast as ast;
40
41pub const DEFINITIONS_TEXT: &str = include_str!("../DEFINITIONS.md");
43
44pub trait Rule: Visitor {
46 fn id(&self) -> &'static str;
53
54 fn description(&self) -> &'static str;
56
57 fn explanation(&self) -> &'static str;
59
60 fn tags(&self) -> TagSet;
62
63 fn url(&self) -> Option<&'static str> {
65 None
66 }
67
68 fn exceptable_nodes(&self) -> Option<&'static [SyntaxKind]>;
72
73 fn related_rules(&self) -> &[&'static str];
79}
80
81pub fn rules() -> Vec<Box<dyn Rule>> {
83 let rules: Vec<Box<dyn Rule>> = vec![
84 Box::<rules::DoubleQuotesRule>::default(),
85 Box::<rules::HereDocCommandsRule>::default(),
86 Box::<rules::SnakeCaseRule>::default(),
87 Box::<rules::RuntimeSectionRule>::default(),
88 Box::<rules::EndingNewlineRule>::default(),
89 Box::<rules::PreambleFormattedRule>::default(),
90 Box::<rules::ParameterMetaMatchedRule>::default(),
91 Box::<rules::WhitespaceRule>::default(),
92 Box::<rules::CommandSectionIndentationRule>::default(),
93 Box::<rules::ImportPlacementRule>::default(),
94 Box::<rules::PascalCaseRule>::default(),
95 Box::<rules::ImportWhitespaceRule>::default(),
96 Box::<rules::MetaSectionsRule>::default(),
97 Box::<rules::ImportSortedRule>::default(),
98 Box::<rules::InputSortedRule>::default(),
99 Box::<rules::LineWidthRule>::default(),
100 Box::<rules::ConsistentNewlinesRule>::default(),
101 Box::<rules::CallInputSpacingRule>::default(),
102 Box::<rules::CallInputKeywordRule>::default(),
103 Box::<rules::SectionOrderingRule>::default(),
104 Box::<rules::DeprecatedObjectRule>::default(),
105 Box::<rules::MetaDescriptionRule>::default(),
106 Box::<rules::DeprecatedPlaceholderRule>::default(),
107 Box::<rules::ExpectedRuntimeKeysRule>::default(),
108 Box::<rules::TodoCommentRule>::default(),
109 Box::<rules::MatchingOutputMetaRule<'_>>::default(),
110 Box::<rules::CommentWhitespaceRule>::default(),
111 Box::<rules::TrailingCommaRule>::default(),
112 Box::<rules::ElementSpacingRule>::default(),
113 Box::<rules::MetaKeyValueFormattingRule>::default(),
114 Box::<rules::ExpressionSpacingRule>::default(),
115 Box::<rules::InputNameRule>::default(),
116 Box::<rules::OutputNameRule>::default(),
117 Box::<rules::DeclarationNameRule>::default(),
118 Box::<rules::RedundantNone>::default(),
119 Box::<rules::ContainerUriRule>::default(),
120 Box::<rules::RequirementsSectionRule>::default(),
121 Box::<rules::KnownRulesRule>::default(),
122 Box::<rules::LintDirectiveValidRule>::default(),
123 Box::<rules::VersionStatementFormattedRule>::default(),
124 Box::<rules::PreambleCommentPlacementRule>::default(),
125 Box::<rules::LintDirectiveFormattedRule>::default(),
126 Box::<rules::ConciseInputRule>::default(),
127 Box::<rules::ShellCheckRule>::default(),
128 Box::<rules::DescriptionLengthRule>::default(),
129 ];
130
131 #[cfg(debug_assertions)]
134 {
135 use std::collections::HashSet;
136
137 use convert_case::Case;
138 use convert_case::Casing;
139 let mut lint_set = HashSet::new();
140 let analysis_set: HashSet<&str> =
141 HashSet::from_iter(analysis::rules().iter().map(|r| r.id()));
142 for r in &rules {
143 if r.id().to_case(Case::Pascal) != r.id() {
144 panic!("lint rule id `{id}` is not pascal case", id = r.id());
145 }
146
147 if !lint_set.insert(r.id()) {
148 panic!("duplicate rule id `{id}`", id = r.id());
149 }
150
151 if analysis_set.contains(r.id()) {
152 panic!("rule id `{id}` is in use by wdl-analysis", id = r.id());
153 }
154 let self_id = &r.id();
155 for related_id in r.related_rules() {
156 if related_id == self_id {
157 panic!(
158 "Rule `{self_id}` refers to itself in its related rules. This is not \
159 allowed."
160 );
161 }
162 }
163 }
164 }
165
166 rules
167}