4"""Classification tables and provider descriptors for ``check_lint_coverage.py``.
6This module is DATA plus pure helpers: it says what kinds of file exist in this
7repository, which of them are code, who is supposed to lint and format each
8kind, and which paths are deliberately outside every checker's reach. The
9enforcement logic that consumes it lives in ``check_lint_coverage.py``.
11The split is deliberate. The tables below are the part a human edits when a new
12file type lands; keeping them away from the subprocess plumbing means that edit
13is reviewable on its own, and means neither file drifts toward being the
142000-line checker nobody reads.
16A NOTE ON WHAT "FORMATTER" MEANS HERE
17-------------------------------------
18Two different things enforce layout in this tree, and both count:
20 * rewriters -- clang-format, gofmt, ruff format, shfmt, cmake-format,
21 just --fmt. Given a file they emit the canonical form, and the format
22 gate runs them in --check mode.
23 * canonical-form checkers -- yamllint's style rules,
24 check_linker_scripts.py. Nothing rewrites a GNU ld script in this
25 ecosystem, so these enforce the layout rules by rejecting deviations
26 instead of by producing the fixed text.
28The distinction that matters for coverage is "does something reject this file
29for being laid out wrongly", not "can something rewrite it for me". A class
30served only by a canonical-form checker is covered; a class served by nothing
31is a gap, and gaps are enumerated in KNOWN_GAPS rather than quietly dropped.
34from __future__
import annotations
36from collections.abc
import Callable
37from dataclasses
import dataclass
64@dataclass(frozen=True)
66 """One file class: what kind it is and what coverage it therefore needs."""
73 def needs_checking(self) -> bool:
74 """True when files of this class must have both a linter and formatter."""
75 return self.kind == CODE
78def _spec(name: str, kind: str, why: str) -> ClassSpec:
79 return ClassSpec(name, kind, why)
85CLASSES: dict[str, ClassSpec] = {
86 "c-family": _spec(
"c-family", CODE,
"firmware, host tools and tests"),
87 "asm": _spec(
"asm", CODE,
"hand-written startup and low-level entry code"),
88 "dockerfile": _spec(
"dockerfile", CODE,
"the devcontainer that pins every CI tool version"),
89 "zsh": _spec(
"zsh", CODE,
"zsh dialect; shellcheck refuses zsh, so not `shell`"),
90 "python": _spec(
"python", CODE,
"the gate suite and host tooling"),
91 "golang": _spec(
"golang", CODE,
"host CLI and conversion policy"),
92 "shell": _spec(
"shell", CODE,
"gate drivers, HIL scripts, git hooks"),
93 "cmake": _spec(
"cmake", CODE,
"decides what compiles with which flags"),
94 "make": _spec(
"make", CODE,
"per-app and top-level build entry points"),
95 "just": _spec(
"just", CODE,
"just task runner recipes"),
96 "linker-script": _spec(
"linker-script", CODE,
"the memory map is code"),
97 "yaml": _spec(
"yaml", CODE,
"workflows decide which gates run at all"),
98 "markdown": _spec(
"markdown", DOC,
"prose; swept by ascii/terminology gates"),
99 "restructuredtext": _spec(
"restructuredtext", DOC,
"prose, vendored doc trees"),
100 "html": _spec(
"html", DATA,
"rendered fixtures and doxygen fragments"),
101 "css": _spec(
"css", DATA,
"e-reader stylesheet assets shipped as content"),
102 "javascript": _spec(
"javascript", DATA,
"doxygen theme assets, not authored logic"),
103 "json": _spec(
"json", CONF,
"manifests and SBOM; consumers validate on load"),
104 "toml": _spec(
"toml", CONF,
"pyproject and friends; ruff validates its own"),
105 "ini": _spec(
"ini", CONF,
"tool configuration"),
106 "xml": _spec(
"xml", DATA,
"EPUB/OPF package descriptors and IDE leftovers"),
107 "csv": _spec(
"csv", DATA,
"measurement tables"),
108 "text": _spec(
"text", DATA,
"baselines, pin lists, licence text"),
109 "binary": _spec(
"binary", DATA,
"images, fonts, archives, PDFs, book fixtures"),
110 "tool-config": _spec(
"tool-config", CONF,
"dotfile config for a named tool"),
111 "vcs-metadata": _spec(
"vcs-metadata", CONF,
"gitignore/gitattributes and kin"),
112 "fixture": _spec(
"fixture", DATA,
"test corpora and golden inputs"),
113 "validated-input": _spec(
116 "exact machine-readable inputs parsed by their pinned build or generator",
118 "generated-source": _spec(
121 "exact reproducible source outputs whose generator owns the canonical bytes",
123 "ansible-systemd-template": _spec(
124 "ansible-systemd-template",
126 "privileged Jinja/systemd input checked against the fleet role contract",
141PATH_CLASS: dict[str, str] = {
142 "infra/ansible/roles/dev_box/templates/ra8-hil-runner.service.j2":
"ansible-systemd-template",
143 "infra/ansible/roles/dev_box/templates/ra8-hil-privileged-policy.json.j2":
"validated-input",
144 "scripts/hil/lib/ra8-hil-privileged.sha256":
"validated-input",
145 "scripts/checks/patches/cppcheck-2.13/misra_9-c23-empty-initializer.patch":
"validated-input",
146 "coprocessor/esp32c6/patches/0001-custom-rpc-sync-response-hook.patch":
"validated-input",
147 "coprocessor/esp32c6/patches/series":
"validated-input",
148 "docs/sbom/patches/levelx/0001-remove-nested-attribute-macros.patch":
"validated-input",
149 "docs/sbom/patches/levelx/series":
"validated-input",
150 "docs/sbom/patches/libwebp/0001-use-ra8-arena-allocator.patch":
"validated-input",
151 "docs/sbom/patches/libwebp/series":
"validated-input",
152 "docs/sbom/patches/miniz/0001-use-ra8-assertion-policy.patch":
"validated-input",
153 "docs/sbom/patches/miniz/series":
"validated-input",
154 "docs/sbom/patches/mbedtls/0001-track-generated-config-headers.patch":
"validated-input",
155 "docs/sbom/patches/mbedtls/series":
"validated-input",
156 "docs/sbom/patches/netxduo/0001-remove-nested-attribute-macros.patch":
"validated-input",
157 "docs/sbom/patches/netxduo/series":
"validated-input",
158 "docs/sbom/patches/protobuf-c/0001-use-ra8-runtime-policy.patch":
"validated-input",
159 "docs/sbom/patches/protobuf-c/series":
"validated-input",
160 "docs/sbom/patches/stb/0001-harden-font-parser-bounds.patch":
"validated-input",
161 "docs/sbom/patches/stb/series":
"validated-input",
162 "docs/sbom/patches/threadx/0001-remove-nested-attribute-macros.patch":
"validated-input",
163 "docs/sbom/patches/threadx/series":
"validated-input",
164 "docs/sbom/patches/usbx/0001-remove-nested-attribute-macros.patch":
"validated-input",
165 "docs/sbom/patches/usbx/series":
"validated-input",
166 "libs/ra8_c6link/proto/ra8_media_download.proto":
"validated-input",
167 "libs/ra8_c6link/inc/ra8_media_download.pb-c.h":
"generated-source",
168 "libs/ra8_c6link/src/ra8_media_download.pb-c.c":
"generated-source",
174EXT_CLASS: dict[str, str] = {
201 ".ld":
"linker-script",
209 ".properties":
"ini",
214 ".user":
"tool-config",
217 ".rst":
"restructuredtext",
268NAME_CLASS: dict[str, str] = {
269 "CMakeLists.txt":
"cmake",
270 "Dockerfile":
"dockerfile",
274 "Doxyfile":
"tool-config",
278 "mimetype":
"fixture",
279 ".clang-format":
"tool-config",
280 ".clang-tidy":
"tool-config",
281 ".clangd":
"tool-config",
282 ".editorconfig":
"tool-config",
283 ".dockerignore":
"tool-config",
284 ".shellcheckrc":
"tool-config",
285 ".pylintrc":
"tool-config",
286 ".globalrc":
"tool-config",
287 ".cursorrules":
"markdown",
288 ".cppcheck-suppressions":
"tool-config",
290 "go.mod":
"tool-config",
291 "go.sum":
"tool-config",
295 "cppcheck_c23_compat.h":
"tool-config",
296 ".style_ignored_dirs":
"tool-config",
297 ".rat-excludes":
"tool-config",
298 ".gitignore":
"vcs-metadata",
299 ".gitattributes":
"vcs-metadata",
300 ".gitmodules":
"vcs-metadata",
301 ".gitkeep":
"vcs-metadata",
302 ".mailmap":
"vcs-metadata",
310SHEBANG_CLASS: tuple[tuple[str, str], ...] = (
311 (
"python",
"python"),
326EXEMPT_PREFIXES: tuple[tuple[str, str], ...] = (
327 (
"libs/third_party/",
"vendored platform SOUP; CLAUDE.md exempts it"),
328 (
"apps/shared_libs/third_party/",
"vendored app SOUP; CLAUDE.md exempts it"),
329 (
"libs/ra8_fonts/",
"generated glyph tables, not hand-authored"),
330 (
"tools/vela/generated/",
"emitted by the Vela NPU compiler on every regen"),
331 (
"docs/reference/",
"committed Renesas datasheet and HUM PDFs"),
332 (
"docs/doxygen_theme/",
"vendored doxygen-awesome theme"),
333 (
"docs/build/",
"generated Doxygen HTML output"),
334 (
"content/",
"EPUB/CBZ book fixtures used as reader test content"),
336 "apps/board/stand_alone/ereader/content/",
337 "EPUB/CBZ book fixtures used as reader test content",
346 (
"tests/fuzz/corpus/",
"libFuzzer corpora, machine-generated random inputs"),
347 (
".devcontainer/p10k.zsh",
"vendored powerlevel10k theme config, not a project script"),
363@dataclass(frozen=True)
365 """What a gap predicate gets to look at when deciding if a file is its own."""
370 """File contents, read lazily and only for files already known uncovered."""
373@dataclass(frozen=True)
375 """One recorded hole in lint/format coverage."""
381 match: Callable[[GapCtx], bool]
384KNOWN_GAPS: tuple[Gap, ...] = (
386 "objc-needs-macos-runner",
389 "the two Objective-C host views (ra8_emulator, ra8_viewer) are AppKit / "
390 "CoreGraphics code. clang-tidy can only parse them against the macOS "
391 "SDK, so clang_tidy.sh claims them on Darwin and not on Linux -- where "
392 "CI runs. The C++ half of #370 is fully closed: every .cpp/.cc finding "
393 "is fixed and its tidy-baseline rows are gone. Closing this one needs a "
394 "macOS lint job, which is a runner decision, not a code change",
395 lambda c: c.cls ==
"c-family" and c.rel.endswith(
".m"),
400def exemption_reason(rel: str) -> str |
None:
401 """Return the justification for `rel` being exempt, or None if it is not."""
402 for prefix, reason
in EXEMPT_PREFIXES:
403 if rel == prefix
or rel.startswith(prefix):
408def validate_tables() -> list[str]:
409 """Assert the tables are internally consistent. Returns a list of problems.
411 A class named by PATH_CLASS/EXT_CLASS/NAME_CLASS but absent from CLASSES would make
412 the gate crash on a file it was supposed to classify, and an exemption with
413 an empty reason is the rot this list exists to prevent.
415 problems: list[str] = []
416 for table_name, table
in (
417 (
"PATH_CLASS", PATH_CLASS),
418 (
"EXT_CLASS", EXT_CLASS),
419 (
"NAME_CLASS", NAME_CLASS),
421 for key, cls
in table.items():
422 if cls
not in CLASSES:
423 problems.append(f
"{table_name}[{key!r}] names unknown class {cls!r}")
424 for _, cls
in SHEBANG_CLASS:
425 if cls
not in CLASSES:
426 problems.append(f
"SHEBANG_CLASS names unknown class {cls!r}")
427 for prefix, reason
in EXEMPT_PREFIXES:
428 if not reason.strip():
429 problems.append(f
"exemption {prefix!r} carries no reason")