Skip to main content

drep/languages/definitions/
c_family.rs

1//! The C family: cppcheck over C and C++, `dotnet format` over C#.
2//!
3//! C and C++ are separate languages - they claim disjoint extensions and the
4//! semantic reviewer's conventions differ - while sharing the one checker:
5//! cppcheck analyzes both, and a project's build files decide how it runs.
6
7use crate::languages::spec::{
8    DEFAULT_TOOL_TIMEOUT_SECS, DiagnosticsStream, LanguageSupport, OutputFormat, ToolSpec,
9};
10
11/// C and C++ deterministic checker.
12///
13/// The config markers are project build files rather than a cppcheck config,
14/// following the gofmt/`go.mod` and clippy/`Cargo.toml` precedent: cppcheck
15/// has no conventional config file of its own, and the presence of a build
16/// system is what says "this project's C is checked here".
17///
18/// SARIF goes to **stderr**: cppcheck leaves stdout nearly empty (progress
19/// chatter only), so reading stdout reports every C file clean.
20///
21/// `--error-exitcode=2` is load-bearing in the other direction: without it
22/// cppcheck exits 0 *with* findings, so the runner's exit-status guard never
23/// fires for it at all. With it, a finding run exits 2 (which parses fine
24/// and stays `Ok`), and a cppcheck build that broke the SARIF stream -
25/// moved it, renamed the format - exits 2 saying nothing parseable, which
26/// is `Unavailable` instead of a permanent silent clean pass.
27pub static CPPCHECK: ToolSpec = ToolSpec {
28    name: "cppcheck",
29    command: &[
30        "cppcheck",
31        "--output-format=sarif",
32        "--enable=warning,style",
33        "--error-exitcode=2",
34    ],
35    local_paths: &[],
36    config_files: &[
37        "CMakeLists.txt",
38        "Makefile",
39        "meson.build",
40        "compile_commands.json",
41    ],
42    config_flag: None,
43    output_format: OutputFormat::Sarif,
44    diagnostics_stream: DiagnosticsStream::Stderr,
45    timeout_secs: DEFAULT_TOOL_TIMEOUT_SECS,
46    timeout_context: None,
47    establishes_compilation: false,
48    serial_in_repository: false,
49    accepts_files: true,
50};
51
52/// C# deterministic checker.
53///
54/// `dotnet format` checks a *project*, not a file list, so it runs bare and
55/// its findings are narrowed to the files being checked afterwards - exactly
56/// as tsc's and clippy's are. The ceiling covers an MSBuild project load,
57/// which can dominate the run on a large solution.
58pub static DOTNET_FORMAT: ToolSpec = ToolSpec {
59    name: "dotnet format",
60    command: &["dotnet", "format", "--verify-no-changes", "--no-restore"],
61    local_paths: &[],
62    // The project marker, not the style file: `dotnet format` must run from
63    // the directory holding the solution or project, and `.editorconfig`
64    // names neither. The same choice gofmt makes with `go.mod` and clippy
65    // with `Cargo.toml`; `.editorconfig` still supplies the rules when the
66    // project has one, and .NET's own defaults when it does not.
67    config_files: &["*.sln", "*.csproj"],
68    config_flag: None,
69    output_format: OutputFormat::Msbuild,
70    diagnostics_stream: DiagnosticsStream::Stdout,
71    timeout_secs: 600,
72    timeout_context: Some(", including its MSBuild project load"),
73    establishes_compilation: false,
74    serial_in_repository: false,
75    accepts_files: false,
76};
77
78/// C language entry.
79pub static C: LanguageSupport = LanguageSupport {
80    name: "c",
81    display_name: "C",
82    extensions: &[".c", ".h"],
83    filenames: &[],
84    filename_prefixes: &[],
85    tools: &[&CPPCHECK],
86    conventions: &[
87        "Buffer overruns and off-by-one indexing into fixed arrays",
88        "Use-after-free, double free, and leaks on early error paths",
89        "Unchecked return values from allocation and system calls",
90        "Signedness confusion and integer overflow in arithmetic",
91        "Data races on shared state without synchronisation",
92    ],
93    vendored_dirs: &[],
94};
95
96/// C++ language entry.
97///
98/// `.h` stays with C and `.hpp`/`.hh`/`.hxx` with C++: a header's language
99/// is convention, not syntax, and the extensions are how every build system
100/// in practice distinguishes them.
101pub static CPP: LanguageSupport = LanguageSupport {
102    name: "cpp",
103    display_name: "C++",
104    extensions: &[".cpp", ".hpp", ".cc", ".hh", ".cxx", ".hxx"],
105    filenames: &[],
106    filename_prefixes: &[],
107    tools: &[&CPPCHECK],
108    conventions: &[
109        "Dangling references and iterators into reallocated containers",
110        "Ownership confusion between raw and smart pointers",
111        "Missing virtual destructors on polymorphic base classes",
112        "Uninitialised members and reads from moved-from state",
113        "Templates instantiated with types that do not satisfy their assumptions",
114    ],
115    vendored_dirs: &[],
116};
117
118/// C# language entry.
119///
120/// No `vendored_dirs`, for the reason `JVM_VENDORED_DIRS` leaves out `out`:
121/// `files::is_ignored_dir` consults the union across every language, so an
122/// entry here skips that directory in repositories with no C# in them at all.
123/// MSBuild's `bin` and `obj` are machine-generated and therefore gitignored in
124/// practice, which the walker already honors on its own - while `bin/` holding
125/// real checked-in scripts is a convention across several ecosystems. Listing
126/// it hid `bin/deploy.sh` from the newly registered Shell language, and the
127/// `RUBOCOP` spec in `ruby.rs` looks for `bin/rubocop`. The cost of listing them
128/// is a silent skip; the benefit is a directory git already ignores.
129pub static CSHARP: LanguageSupport = LanguageSupport {
130    name: "csharp",
131    display_name: "C#",
132    extensions: &[".cs"],
133    filenames: &[],
134    filename_prefixes: &[],
135    tools: &[&DOTNET_FORMAT],
136    conventions: &[
137        "async void, and tasks that are never awaited",
138        "IDisposable not disposed on every path",
139        "Null dereferences the nullable flow analysis would catch",
140        "Closures capturing a loop variable's stale value",
141        "Struct copies where a reference was intended",
142    ],
143    vendored_dirs: &[],
144};
145
146/// The family's entries in registration order. See `ALL_LANGUAGES`.
147pub(crate) static FAMILY: &[&LanguageSupport] = &[&C, &CPP, &CSHARP];