3"""Canonical generated/file-size waiver grammar shared by both gates."""
5from __future__
import annotations
8from dataclasses
import dataclass
9from pathlib
import Path, PurePosixPath
11from suppression_catalog
import ownership
12from suppression_model
import Finding, Suppression
15_COMMENT =
r"(?:#|//|/\*+|\*)"
16_GENERATED_RE = re.compile(
17 rf
"^\s*{_COMMENT}\s*@generated\s+by\s+"
18 r"(?P<generator>[A-Za-z0-9_.-]+(?:/[A-Za-z0-9_.-]+)+)\s+"
19 r"(?P<reason>\S.*?)\s*(?:\*/)?\s*$",
22_FILE_SIZE_RE = re.compile(rf
"^\s*{_COMMENT}\s*FILE-SIZE-OK\s*:\s*(?P<reason>\S.*?)\s*(?:\*/)?\s*$")
23_GENERATED_HINT_RE = re.compile(rf
"^\s*{_COMMENT}\s*@generated\b", re.IGNORECASE)
24_FILE_SIZE_HINT_RE = re.compile(rf
"^\s*{_COMMENT}\s*FILE-SIZE-OK\b", re.IGNORECASE)
27@dataclass(frozen=True)
29 """One canonical head marker and its bound provenance."""
37def parse_head_waivers(text: str) -> tuple[list[HeadWaiver], list[Finding]]:
38 """Parse exact standalone comment markers in the first forty lines."""
39 waivers: list[HeadWaiver] = []
40 findings: list[Finding] = []
41 for line_no, raw
in enumerate(text.splitlines()[:HEAD_SCAN_LINES], start=1):
42 generated = _GENERATED_RE.fullmatch(raw)
43 file_size = _FILE_SIZE_RE.fullmatch(raw)
44 if generated
is not None:
45 generator = generated.group(
"generator")
46 normalized = PurePosixPath(generator)
47 if normalized.is_absolute()
or ".." in normalized.parts:
49 Finding(
"malformed-generated-marker",
"unsafe generator path", line=line_no)
52 reason = generated.group(
"reason").strip()
53 if not _substantive_reason(reason):
56 "non-substantive-waiver-reason",
57 "generated marker rationale must contain a letter or digit",
62 waivers.append(HeadWaiver(line_no,
"generated", generator, reason))
63 elif file_size
is not None:
64 reason = file_size.group(
"reason").strip()
65 if not _substantive_reason(reason):
68 "non-substantive-waiver-reason",
69 "file-size rationale must contain a letter or digit",
74 waivers.append(HeadWaiver(line_no,
"file-size",
"", reason))
75 elif _GENERATED_HINT_RE.match(raw)
or _FILE_SIZE_HINT_RE.match(raw):
78 "malformed-generated-marker",
79 "directive-like waiver does not match the canonical standalone grammar",
86 "duplicate-file-size-waiver",
87 "a file head may carry only one generated/file-size waiver",
91 return waivers, findings
94def _substantive_reason(reason: str) -> bool:
95 """Reject empty and punctuation/delimiter-only waiver rationales."""
96 return any(character.isalnum()
for character
in reason)
99def effective_head_waiver(
102 tracked_paths: frozenset[str] |
None =
None,
103 artifact_path: str =
"",
104) -> tuple[HeadWaiver |
None, list[Finding]]:
105 """Return the sole valid waiver, rejecting untracked generated provenance."""
106 waivers, findings = parse_head_waivers(text)
107 if len(waivers) != 1:
108 return None, findings
110 if waiver.kind ==
"generated" and artifact_path
and waiver.generator == artifact_path:
113 "self-generated-provenance",
114 "generated artifact must name a distinct generator",
118 return None, findings
119 if waiver.kind ==
"generated" and not _has_substantive_generated_body(text):
122 "generated-marker-without-body",
123 "generated marker has no substantive generated body",
127 return None, findings
129 waiver.kind ==
"generated"
130 and tracked_paths
is not None
131 and waiver.generator
not in tracked_paths
135 "untracked-generated-provenance",
136 f
"generator is not tracked: {waiver.generator}",
140 return None, findings
141 return waiver, findings
144def _has_substantive_generated_body(text: str) -> bool:
145 """Require code/data beyond blank lines and standalone comment prose."""
146 without_blocks = _without_block_comments(text)
147 for raw
in without_blocks.splitlines():
149 if not line
or line.startswith((
"#",
"//",
"*")):
155def _without_block_comments(text: str) -> str:
156 """Remove closed or unterminated C block comments without executing a lexer."""
157 parts: list[str] = []
159 while cursor < len(text):
160 start = text.find(
"/*", cursor)
162 parts.append(text[cursor:])
164 parts.append(text[cursor:start])
165 end = text.find(
"*/", start + 2)
169 return "".join(parts)
172def _same_file_identity(artifact: Path, generator: Path) -> bool:
173 """Return whether two resolved paths name one filesystem object."""
174 if artifact == generator:
177 artifact_stat = artifact.stat()
178 generator_stat = generator.stat()
181 return (artifact_stat.st_dev, artifact_stat.st_ino) == (
182 generator_stat.st_dev,
183 generator_stat.st_ino,
187def _generator_identity_finding(
188 artifact: Path, generator_rel: str, repo_root: Path
190 """Reject symlinked, missing, out-of-repo, nonregular, and self-identical generators."""
191 generator = repo_root / generator_rel
192 if generator.is_symlink():
194 "symlinked-generated-provenance",
195 f
"generator must be a regular repository file, not a symlink: {generator_rel}",
198 resolved = generator.resolve(strict=
True)
199 resolved.relative_to(repo_root.resolve(strict=
True))
200 regular = resolved.is_file()
201 except (OSError, ValueError):
204 if resolved
is None or not regular:
206 "missing-generated-provenance",
207 f
"generator is not a regular repository file: {generator_rel}",
209 if _same_file_identity(artifact.resolve(), resolved):
211 "self-generated-provenance",
212 f
"generator and artifact share one file identity: {generator_rel}",
217def generated_records(
220 tracked_paths: frozenset[str],
222) -> tuple[list[Suppression], list[Finding]]:
223 """Inventory one canonical generated artifact, never marker prose/templates."""
224 waiver, findings = effective_head_waiver(text, tracked_paths=tracked_paths, artifact_path=path)
225 if waiver
is None or waiver.kind !=
"generated":
226 return [], [Finding(item.code, item.message, path, item.line)
for item
in findings]
227 identity_problem = _generator_identity_finding(repo_root / path, waiver.generator, repo_root)
228 if identity_problem
is not None:
229 findings.append(Finding(identity_problem.code, identity_problem.message, line=waiver.line))
230 return [], [Finding(item.code, item.message, path, item.line)
for item
in findings]
231 record = Suppression(
235 "generated-artifact",
241 f
"generator:{waiver.generator}",
245 located = [Finding(item.code, item.message, path, item.line)
for item
in findings]
246 return [record], located
249def path_has_effective_waiver(
252 tracked_paths: frozenset[str],
254 """Apply the shared grammar for the file-size gate's real filesystem path."""
256 text = path.read_text(encoding=
"utf-8", errors=
"replace")
261 path.resolve(strict=
True).relative_to(repo_root.resolve(strict=
True)).as_posix()
264 artifact_path = path.name
267 waiver, findings = effective_head_waiver(
268 text, tracked_paths=tracked_paths, artifact_path=artifact_path
270 if waiver
is None or findings:
272 if waiver.kind ==
"file-size":
274 return _generator_identity_finding(path, waiver.generator, repo_root)
is None