1use super::spec::{DEFAULT_TOOL_TIMEOUT_SECS, LanguageSupport, ToolSpec};
11
12static JVM_VENDORED_DIRS: &[&str] = &["build", ".gradle", "target"];
24
25pub static RUFF: ToolSpec = ToolSpec {
27 name: "ruff",
28 command: &["ruff", "check", "--output-format", "json"],
29 local_paths: &["venv/bin/ruff", ".venv/bin/ruff"],
30 config_files: &["pyproject.toml", "ruff.toml", ".ruff.toml"],
31 config_flag: None,
32 output_format: "json",
33 diagnostics_stream: "stdout",
34 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
35 timeout_context: None,
36 establishes_compilation: false,
37 serial_in_repository: false,
38 accepts_files: true,
39};
40
41pub static ESLINT: ToolSpec = ToolSpec {
43 name: "eslint",
44 command: &["eslint", "--format", "json"],
45 local_paths: &["node_modules/.bin/eslint"],
46 config_files: &[
47 "eslint.config.js",
48 "eslint.config.mjs",
49 "eslint.config.cjs",
50 ".eslintrc",
51 ".eslintrc.js",
52 ".eslintrc.cjs",
53 ".eslintrc.json",
54 ".eslintrc.yml",
55 ".eslintrc.yaml",
56 ],
57 config_flag: None,
58 output_format: "json",
59 diagnostics_stream: "stdout",
60 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
61 timeout_context: None,
62 establishes_compilation: false,
63 serial_in_repository: false,
64 accepts_files: true,
65};
66
67pub static TSC: ToolSpec = ToolSpec {
69 name: "tsc",
70 command: &["tsc", "--noEmit", "--pretty", "false"],
71 local_paths: &["node_modules/.bin/tsc"],
72 config_files: &["tsconfig.json"],
73 config_flag: None,
74 output_format: "tsc",
75 diagnostics_stream: "stdout",
76 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
77 timeout_context: None,
78 establishes_compilation: true,
79 serial_in_repository: false,
80 accepts_files: false,
83};
84
85pub static GOFMT: ToolSpec = ToolSpec {
90 name: "gofmt",
91 command: &["gofmt", "-l"],
92 local_paths: &[],
93 config_files: &["go.mod"],
94 config_flag: None,
95 output_format: "lines",
96 diagnostics_stream: "stdout",
97 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
98 timeout_context: None,
99 establishes_compilation: false,
100 serial_in_repository: false,
101 accepts_files: true,
102};
103
104pub static GO_VET: ToolSpec = ToolSpec {
109 name: "go vet",
110 command: &["go", "vet"],
111 local_paths: &[],
112 config_files: &["go.mod"],
113 config_flag: None,
114 output_format: "position",
115 diagnostics_stream: "stderr",
116 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
117 timeout_context: None,
118 establishes_compilation: true,
119 serial_in_repository: false,
120 accepts_files: true,
121};
122
123pub static CLIPPY: ToolSpec = ToolSpec {
125 name: "clippy",
126 command: &["cargo", "clippy", "--message-format", "json", "--quiet"],
127 local_paths: &[],
128 config_files: &["Cargo.toml"],
129 config_flag: None,
130 output_format: "cargo",
131 diagnostics_stream: "stdout",
132 timeout_secs: 1_800,
136 timeout_context: Some(", including its Cargo build-lock wait"),
137 establishes_compilation: true,
138 serial_in_repository: true,
139 accepts_files: false,
142};
143
144pub static CHECKSTYLE: ToolSpec = ToolSpec {
151 name: "checkstyle",
152 command: &["checkstyle", "-f", "sarif"],
153 local_paths: &[],
154 config_files: &[
155 "checkstyle.xml",
156 ".checkstyle.xml",
157 "config/checkstyle/checkstyle.xml",
158 "gradle/config/checkstyle/checkstyle.xml",
159 ],
160 config_flag: Some("-c"),
161 output_format: "sarif",
162 diagnostics_stream: "stdout",
163 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
166 timeout_context: None,
167 establishes_compilation: false,
170 serial_in_repository: false,
171 accepts_files: true,
172};
173
174pub static KTLINT: ToolSpec = ToolSpec {
180 name: "ktlint",
181 command: &["ktlint", "--log-level=none", "--reporter=json"],
182 local_paths: &[],
183 config_files: &[".editorconfig"],
186 config_flag: None,
187 output_format: "ktlint",
188 diagnostics_stream: "stdout",
189 timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
190 timeout_context: None,
191 establishes_compilation: false,
192 serial_in_repository: false,
193 accepts_files: true,
194};
195
196pub static PYTHON: LanguageSupport = LanguageSupport {
198 name: "python",
199 display_name: "Python",
200 extensions: &[".py"],
201 tools: &[&RUFF],
202 conventions: &[
203 "Follows PEP 8 naming and structure",
204 "Type hints on public APIs, and correct use of Optional/None",
205 "Context managers for resources rather than manual cleanup",
206 "Mutable default arguments, and late-binding closures in loops",
207 ],
208 vendored_dirs: &["__pycache__", "venv", ".venv", "env", ".tox", ".eggs"],
209};
210
211pub static JAVASCRIPT: LanguageSupport = LanguageSupport {
213 name: "javascript",
214 display_name: "JavaScript",
215 extensions: &[".js", ".jsx", ".mjs", ".cjs"],
216 tools: &[&ESLINT],
217 conventions: &[
218 "Unhandled promise rejections and missing await",
219 "Sequential awaits in a loop where the work is independent",
220 "var versus let/const, and accidental global scope",
221 "Equality coercion (== versus ===)",
222 ],
223 vendored_dirs: &["node_modules", ".next", ".nuxt"],
224};
225
226pub static TYPESCRIPT: LanguageSupport = LanguageSupport {
228 name: "typescript",
229 display_name: "TypeScript",
230 extensions: &[".ts", ".tsx", ".mts", ".cts"],
231 tools: &[&ESLINT, &TSC],
232 conventions: &[
233 "`any` where a real type is available, and unsafe casts",
234 "Unhandled promise rejections and missing await",
235 "Non-null assertions (!) that hide a genuine null case",
236 "Sequential awaits in a loop where the work is independent",
237 ],
238 vendored_dirs: &["node_modules", ".next", ".nuxt"],
239};
240
241pub static GO: LanguageSupport = LanguageSupport {
243 name: "go",
244 display_name: "Go",
245 extensions: &[".go"],
246 tools: &[&GOFMT, &GO_VET],
247 conventions: &[
248 "Errors ignored rather than checked and wrapped",
249 "Goroutine leaks, and writes to a channel nobody reads",
250 "defer inside a loop, and defer that never runs",
251 "Data races on shared state without synchronisation",
252 ],
253 vendored_dirs: &["vendor"],
254};
255
256pub static RUST_LANG: LanguageSupport = LanguageSupport {
258 name: "rust",
259 display_name: "Rust",
260 extensions: &[".rs"],
261 tools: &[&CLIPPY],
262 conventions: &[
263 "unwrap/expect on values that can legitimately be None or Err",
264 "unsafe blocks, and whether their invariants are documented",
265 "Unnecessary clones and allocations in hot paths",
266 "Send/Sync correctness for types crossing threads",
267 ],
268 vendored_dirs: &["target"],
269};
270
271pub static JAVA: LanguageSupport = LanguageSupport {
273 name: "java",
274 display_name: "Java",
275 extensions: &[".java"],
276 tools: &[&CHECKSTYLE],
277 conventions: &[
278 "Resources closed on every path, and try-with-resources where it applies",
279 "Null handling: Optional versus a nullable return, and unchecked dereferences",
280 "equals/hashCode/compareTo consistency, and mutable state in a shared object",
281 "Exceptions swallowed or logged and rethrown, losing the original cause",
282 "Concurrency: unsynchronised shared state, and non-thread-safe fields on a singleton",
283 ],
284 vendored_dirs: JVM_VENDORED_DIRS,
285};
286
287pub static KOTLIN: LanguageSupport = LanguageSupport {
292 name: "kotlin",
293 display_name: "Kotlin",
294 extensions: &[".kt", ".kts"],
295 tools: &[&KTLINT],
296 conventions: &[
297 "Platform types from Java interop dereferenced without a null check",
298 "runBlocking on a coroutine path, and scopes that outlive their work",
299 "!! where the null case is real, and lateinit read before assignment",
300 "data class equality over mutable properties",
301 ],
302 vendored_dirs: JVM_VENDORED_DIRS,
303};
304
305pub static SCALA: LanguageSupport = LanguageSupport {
312 name: "scala",
313 display_name: "Scala",
314 extensions: &[".scala", ".sc"],
315 tools: &[],
316 conventions: &[
317 "Partial functions and non-exhaustive matches",
318 "Option/Either handling versus get and head on an empty collection",
319 "Implicits whose resolution is not obvious at the call site",
320 "Futures without an explicit ExecutionContext, and blocking inside one",
321 ],
322 vendored_dirs: JVM_VENDORED_DIRS,
323};
324
325pub static GROOVY: LanguageSupport = LanguageSupport {
331 name: "groovy",
332 display_name: "Groovy",
333 extensions: &[".groovy", ".gradle"],
334 tools: &[],
335 conventions: &[
336 "Dynamic dispatch where a typed call would fail at compile time",
337 "Gradle configuration-time work that belongs in a task action",
338 "Dependency and plugin versions pinned versus floating",
339 "String interpolation of values that should be escaped",
340 ],
341 vendored_dirs: JVM_VENDORED_DIRS,
342};
343
344pub static ALL_LANGUAGES: &[&LanguageSupport] = &[
346 &PYTHON,
347 &JAVASCRIPT,
348 &TYPESCRIPT,
349 &GO,
350 &RUST_LANG,
351 &JAVA,
352 &KOTLIN,
353 &SCALA,
354 &GROOVY,
355];