Skip to main content

drep/languages/
definitions.rs

1//! The registered languages.
2//!
3//! Adding a language is an entry here plus, if it has one, a tool output parser.
4//! No control flow anywhere else in drep changes.
5//!
6//! `config_files` is what makes a tool run at all: drep checks a project against
7//! the style that project has *chosen*, so a repo with no eslint config gets no
8//! eslint findings rather than a wall of default-preset complaints.
9
10use super::spec::{LanguageSupport, ToolSpec};
11
12/// Python deterministic checker.
13pub static RUFF: ToolSpec = ToolSpec {
14    name: "ruff",
15    command: &["ruff", "check", "--output-format", "json"],
16    local_paths: &["venv/bin/ruff", ".venv/bin/ruff"],
17    config_files: &["pyproject.toml", "ruff.toml", ".ruff.toml"],
18    output_format: "json",
19    diagnostics_stream: "stdout",
20    accepts_files: true,
21};
22
23/// JavaScript deterministic checker.
24pub static ESLINT: ToolSpec = ToolSpec {
25    name: "eslint",
26    command: &["eslint", "--format", "json"],
27    local_paths: &["node_modules/.bin/eslint"],
28    config_files: &[
29        "eslint.config.js",
30        "eslint.config.mjs",
31        "eslint.config.cjs",
32        ".eslintrc",
33        ".eslintrc.js",
34        ".eslintrc.cjs",
35        ".eslintrc.json",
36        ".eslintrc.yml",
37        ".eslintrc.yaml",
38    ],
39    output_format: "json",
40    diagnostics_stream: "stdout",
41    accepts_files: true,
42};
43
44/// TypeScript's compiler-as-checker. Streams diagnostics to stdout.
45pub static TSC: ToolSpec = ToolSpec {
46    name: "tsc",
47    command: &["tsc", "--noEmit", "--pretty", "false"],
48    local_paths: &["node_modules/.bin/tsc"],
49    config_files: &["tsconfig.json"],
50    output_format: "tsc",
51    diagnostics_stream: "stdout",
52    accepts_files: true,
53};
54
55/// Go formatting checker - lists files whose formatting drifts from `gofmt`'s.
56///
57/// `go.mod` is the marker that this is a Go module at all; gofmt has no
58/// config of its own because its formatting is not configurable.
59pub static GOFMT: ToolSpec = ToolSpec {
60    name: "gofmt",
61    command: &["gofmt", "-l"],
62    local_paths: &[],
63    config_files: &["go.mod"],
64    output_format: "lines",
65    diagnostics_stream: "stdout",
66    accepts_files: true,
67};
68
69/// `go vet`. Streams diagnostics to stderr.
70///
71/// Not -json: that only emits JSON once the package compiles, and a package
72/// that does not compile is exactly when vet has the most to say.
73pub static GO_VET: ToolSpec = ToolSpec {
74    name: "go vet",
75    command: &["go", "vet"],
76    local_paths: &[],
77    config_files: &["go.mod"],
78    output_format: "position",
79    diagnostics_stream: "stderr",
80    accepts_files: true,
81};
82
83/// Rust linter - emits structured JSON via cargo's message-format.
84pub static CLIPPY: ToolSpec = ToolSpec {
85    name: "clippy",
86    command: &["cargo", "clippy", "--message-format", "json", "--quiet"],
87    local_paths: &[],
88    config_files: &["Cargo.toml"],
89    output_format: "cargo",
90    diagnostics_stream: "stdout",
91    // `cargo clippy` checks a crate, not files: a path argument is rejected
92    // with "unexpected argument". See `ToolSpec::accepts_files`.
93    accepts_files: false,
94};
95
96/// Python language entry.
97pub static PYTHON: LanguageSupport = LanguageSupport {
98    name: "python",
99    display_name: "Python",
100    extensions: &[".py"],
101    tools: &[&RUFF],
102    conventions: &[
103        "Follows PEP 8 naming and structure",
104        "Type hints on public APIs, and correct use of Optional/None",
105        "Context managers for resources rather than manual cleanup",
106        "Mutable default arguments, and late-binding closures in loops",
107    ],
108    vendored_dirs: &["__pycache__", "venv", ".venv", "env", ".tox", ".eggs"],
109};
110
111/// JavaScript language entry.
112pub static JAVASCRIPT: LanguageSupport = LanguageSupport {
113    name: "javascript",
114    display_name: "JavaScript",
115    extensions: &[".js", ".jsx", ".mjs", ".cjs"],
116    tools: &[&ESLINT],
117    conventions: &[
118        "Unhandled promise rejections and missing await",
119        "Sequential awaits in a loop where the work is independent",
120        "var versus let/const, and accidental global scope",
121        "Equality coercion (== versus ===)",
122    ],
123    vendored_dirs: &["node_modules", ".next", ".nuxt"],
124};
125
126/// TypeScript language entry.
127pub static TYPESCRIPT: LanguageSupport = LanguageSupport {
128    name: "typescript",
129    display_name: "TypeScript",
130    extensions: &[".ts", ".tsx", ".mts", ".cts"],
131    tools: &[&ESLINT, &TSC],
132    conventions: &[
133        "`any` where a real type is available, and unsafe casts",
134        "Unhandled promise rejections and missing await",
135        "Non-null assertions (!) that hide a genuine null case",
136        "Sequential awaits in a loop where the work is independent",
137    ],
138    vendored_dirs: &["node_modules", ".next", ".nuxt"],
139};
140
141/// Go language entry.
142pub static GO: LanguageSupport = LanguageSupport {
143    name: "go",
144    display_name: "Go",
145    extensions: &[".go"],
146    tools: &[&GOFMT, &GO_VET],
147    conventions: &[
148        "Errors ignored rather than checked and wrapped",
149        "Goroutine leaks, and writes to a channel nobody reads",
150        "defer inside a loop, and defer that never runs",
151        "Data races on shared state without synchronisation",
152    ],
153    vendored_dirs: &["vendor"],
154};
155
156/// Rust language entry.
157pub static RUST_LANG: LanguageSupport = LanguageSupport {
158    name: "rust",
159    display_name: "Rust",
160    extensions: &[".rs"],
161    tools: &[&CLIPPY],
162    conventions: &[
163        "unwrap/expect on values that can legitimately be None or Err",
164        "unsafe blocks, and whether their invariants are documented",
165        "Unnecessary clones and allocations in hot paths",
166        "Send/Sync correctness for types crossing threads",
167    ],
168    vendored_dirs: &["target"],
169};
170
171/// Every registered language, in registration order.
172pub static ALL_LANGUAGES: &[&LanguageSupport] =
173    &[&PYTHON, &JAVASCRIPT, &TYPESCRIPT, &GO, &RUST_LANG];