v0.1.0-pre.011-fix-01
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# file: scripts/audit_rust_general_rules.py
|
||||
# version: 4
|
||||
# version: 5
|
||||
|
||||
"""Audit mechanically verifiable Rust rules shared by all Rust projects."""
|
||||
|
||||
@@ -45,6 +45,11 @@ def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]:
|
||||
text = path.read_text(encoding="utf-8")
|
||||
lines = text.splitlines()
|
||||
violations: list[Violation] = []
|
||||
local_modules = {
|
||||
match.group(1)
|
||||
for line in lines
|
||||
if (match := re.fullmatch(r"\s*mod\s+([A-Za-z_][A-Za-z0-9_]*)\s*;", line)) is not None
|
||||
}
|
||||
expected_header = f"// file: {relative}"
|
||||
if not lines or lines[0] != expected_header:
|
||||
violations.append(Violation("RUST001", relative, 1, f"expected `{expected_header}`"))
|
||||
@@ -54,16 +59,13 @@ def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]:
|
||||
violations.append(Violation("RUST003", relative, max(len(lines), 1), "file must end with exactly one newline"))
|
||||
for index, line in enumerate(lines, 1):
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("pub mod ") and (
|
||||
relative == "kb-lib/src/lib.rs" and stripped == "pub mod materializer;"
|
||||
or relative.startswith("kb-lib/src/materializer")
|
||||
):
|
||||
if stripped.startswith("pub mod "):
|
||||
violations.append(
|
||||
Violation(
|
||||
"RUST014",
|
||||
relative,
|
||||
index,
|
||||
"consolidated materializer modules must remain private",
|
||||
"modules must remain private and APIs must use explicit re-exports",
|
||||
)
|
||||
)
|
||||
if re.match(r"^(?:pub(?:\(crate\))?\s+)?use\s+", stripped) is None:
|
||||
@@ -78,34 +80,62 @@ def audit_file(root: pathlib.Path, path: pathlib.Path) -> list[Violation]:
|
||||
if "{" in stripped or "}" in stripped:
|
||||
code = "RUST011" if is_export else "RUST013"
|
||||
violations.append(Violation(code, relative, index, "grouped use declarations are forbidden"))
|
||||
if path.name in {"lib.rs", "main.rs"}:
|
||||
for attribute in ("#![warn(missing_docs)]", "#![deny(unreachable_pub)]", "#![forbid(unsafe_code)]"):
|
||||
if attribute not in lines[:20]:
|
||||
violations.append(Violation("RUST020", relative, 1, f"missing `{attribute}`"))
|
||||
previous_export_line: int | None = None
|
||||
previous_export_visibility: str | None = None
|
||||
for index, line in enumerate(lines, 1):
|
||||
if not (line.startswith("pub use ") or line.startswith("pub(crate) use ")):
|
||||
continue
|
||||
previous = lines[index - 2].strip() if index >= 2 else ""
|
||||
if not previous.startswith("///"):
|
||||
violations.append(Violation("RUST021", relative, index, "crate-root re-export requires adjacent rustdoc"))
|
||||
internal_re_export = re.match(
|
||||
r"^pub(?:\(crate\))?\s+use\s+(?:crate::)?([A-Za-z_][A-Za-z0-9_]*)::",
|
||||
stripped,
|
||||
)
|
||||
if is_export and (
|
||||
re.match(r"^pub(?:\(crate\))?\s+use\s+crate::", stripped)
|
||||
or (
|
||||
internal_re_export is not None
|
||||
and internal_re_export.group(1) in local_modules
|
||||
)
|
||||
):
|
||||
violations.append(Violation("RUST015", relative, index, "internal re-export must start with `self::`"))
|
||||
if is_export and re.search(r"\s+as\s+[A-Za-z_][A-Za-z0-9_]*\s*;", stripped):
|
||||
violations.append(Violation("RUST016", relative, index, "re-export aliases are forbidden"))
|
||||
previous_export_line: int | None = None
|
||||
previous_export_visibility: str | None = None
|
||||
blank_since_export = False
|
||||
for index, line in enumerate(lines, 1):
|
||||
if not line.strip():
|
||||
if previous_export_line is not None:
|
||||
blank_since_export = True
|
||||
continue
|
||||
if line.startswith("///"):
|
||||
continue
|
||||
if line.startswith("pub use ") or line.startswith("pub(crate) use "):
|
||||
visibility = "pub(crate)" if line.startswith("pub(crate) use ") else "pub"
|
||||
if (
|
||||
previous_export_line is not None
|
||||
and previous_export_visibility == visibility
|
||||
and any(not candidate.strip() for candidate in lines[previous_export_line:index - 1])
|
||||
and blank_since_export
|
||||
):
|
||||
violations.append(
|
||||
Violation(
|
||||
"RUST022",
|
||||
relative,
|
||||
index,
|
||||
"crate-root re-export block contains an empty line",
|
||||
"re-export block contains an empty line",
|
||||
)
|
||||
)
|
||||
previous_export_line = index
|
||||
previous_export_visibility = visibility
|
||||
blank_since_export = False
|
||||
continue
|
||||
previous_export_line = None
|
||||
previous_export_visibility = None
|
||||
blank_since_export = False
|
||||
if path.name in {"lib.rs", "main.rs"}:
|
||||
for attribute in ("#![warn(missing_docs)]", "#![deny(unreachable_pub)]", "#![forbid(unsafe_code)]"):
|
||||
if attribute not in lines[:20]:
|
||||
violations.append(Violation("RUST020", relative, 1, f"missing `{attribute}`"))
|
||||
for index, line in enumerate(lines, 1):
|
||||
if not (line.startswith("pub use ") or line.startswith("pub(crate) use ")):
|
||||
continue
|
||||
previous = lines[index - 2].strip() if index >= 2 else ""
|
||||
if not previous.startswith("///"):
|
||||
violations.append(Violation("RUST021", relative, index, "crate-root re-export requires adjacent rustdoc"))
|
||||
return violations
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user