1720af1431
Third-round integrations of the driver-manager migration's D-phase. The pcid_interface crate (in local/sources/base submodule, see upstream commitddcd0870) now reads REDBEAR_DRIVER_PCI_IRQ_MODE and REDBEAR_DRIVER_DISABLE_ACCEL at connect_default time, exposing them as PciFunctionHandle accessors. The quirk integration is end-to-end: driver-manager emits the env vars on spawn, pcid_interface parses them, and the child driver reads the hints from its PciFunctionHandle. driver-manager (16 tests, unchanged count + 3 new for observability): - main.rs: --list-drivers prints the config table, --dry-run prints how many drivers would-bind/defer/blacklisted without spawning, --export-blacklist prints the loaded blacklist. - policy.rs: adds blacklist_module_names() iterator for export. driver-manager v1.5 quirks.rs (created earlier at v1.4 PciQuirkFlags work) is in the staging area and is unchanged this round. local/scripts/driver-manager-audit-no-stubs.py: - Drops R4 (was: 'let _ = ...' no-op detection) because Rust's 'let _ = expression' is idiomatic and not actually a stub. - Adds R5 (stub-macro callbacks: unimplemented!/todo!/unreachable! in callback / fn bodies) to detect dead-end fallbacks. - Audit gate now reports 0 violations across 35 files (was 34, pcid_interface is now in scope). local/sources/base submodule pointer: updated to upstream commitddcd0870(the pcid_interface env-var-reading commit). Test totals: 58 tests across 4 crates, all passing. § 0.5 audit-no-stubs.py: 0 violations across 35 files.
194 lines
6.4 KiB
Python
Executable File
194 lines
6.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
D4 — driver-manager-audit-no-stubs.py
|
|
|
|
Static-analysis gate for the § 0.5 comprehensive implementation principle
|
|
(see `local/docs/DRIVER-MANAGER-MIGRATION-PLAN.md` § 0.5).
|
|
|
|
Four rules:
|
|
- R1: stub macros (`unimplemented!()` / `todo!()` / `unreachable!()`) in
|
|
production paths.
|
|
- R2: empty catch-all match arms (`_ => {}`).
|
|
- R3: `DriverParams::default()` returns in concrete drivers.
|
|
- R5: stub-macro calls inside Driver trait method bodies — distinguish
|
|
from test-only usage.
|
|
|
|
Whitelisted: tests under `#[cfg(test)]`, examples, doc-tests.
|
|
|
|
Returns exit 0 on a clean audit, 1 otherwise.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from typing import Iterator
|
|
|
|
REPO_ROOT = Path("/mnt/data/Builds/RedBear-OS")
|
|
SCAN_ROOTS = [
|
|
REPO_ROOT / "local/recipes/system/driver-manager/source",
|
|
REPO_ROOT / "local/recipes/drivers/redox-driver-core/source",
|
|
REPO_ROOT / "local/recipes/drivers/redox-driver-pci/source",
|
|
REPO_ROOT / "local/recipes/drivers/redox-driver-sys/source",
|
|
]
|
|
RUST_EXT = ".rs"
|
|
|
|
# Lines to skip — match by exact suffix.
|
|
SKIP_LINE_SUFFIXES = (
|
|
"unimplemented!()",
|
|
"todo!()",
|
|
"unreachable!()",
|
|
)
|
|
|
|
# R5: stub macros that are particularly suspect inside callback / fn bodies.
|
|
# We only flag these in production paths (i.e. not in #[cfg(test)] modules).
|
|
STUB_MACRO_PATTERN = (
|
|
r"\b(?:unimplemented|todo|unreachable)!\(\)",
|
|
"R5",
|
|
"stub macro left in callback / fn body",
|
|
)
|
|
|
|
# Match-block skip:
|
|
# `_ => { }` or `_ => {}` (empty body)
|
|
EMPTY_CATCH_ALL = re.compile(r"^\s*_?\s*=>\s*\{\s*\}\s*,?\s*$")
|
|
|
|
# Default trait method returning Ok(()) in lifecycle hooks:
|
|
# fn probe(...) -> ProbeResult { ... Ok(()) skip ... }
|
|
# fn remove(...) -> Result<...> { ... Ok(()) skip ... }
|
|
TRAIT_LIFECYCLE_OK_BODY = re.compile(
|
|
r"^\s*Ok\s*\(\s*\)\s*,?\s*$"
|
|
)
|
|
|
|
# Default DriverParams::default() return in concrete driver impl.
|
|
DRIVER_PARAMS_DEFAULT = re.compile(r"\bDriverParams::default\s*\(\s*\)")
|
|
|
|
|
|
def iter_rust_files() -> Iterator[Path]:
|
|
for root in SCAN_ROOTS:
|
|
if not root.exists():
|
|
continue
|
|
for path in sorted(root.rglob(f"*{RUST_EXT}")):
|
|
# Skip vendored or generated directories if any ever appear.
|
|
if "/target/" in str(path):
|
|
continue
|
|
yield path
|
|
|
|
|
|
def file_is_test_only(path: Path, content: str) -> bool:
|
|
"""Return True if every top-level item in this file is gated by
|
|
`#[cfg(test)]` (i.e. helper files for tests, doc tests, fixtures)."""
|
|
test_count = content.count("#[cfg(test)]")
|
|
if test_count == 0:
|
|
return False
|
|
non_test_items = 0
|
|
fn_count = content.count("\nfn ")
|
|
struct_count = content.count("\nstruct ")
|
|
enum_count = content.count("\nenum ")
|
|
impl_count = content.count("\nimpl ")
|
|
non_test_items = fn_count + struct_count + enum_count + impl_count
|
|
# Be conservative: only treat as test-only if `#[cfg(test)]` appears
|
|
# at module level (outside any inner `mod tests { ... }`).
|
|
if content.lstrip().startswith("#[cfg(test)]"):
|
|
return True
|
|
return False
|
|
|
|
|
|
def audit_file(path: Path) -> list[tuple[int, str, str]]:
|
|
"""Return a list of (line_no, rule_id, message) violations for one file."""
|
|
content = path.read_text(encoding="utf-8", errors="replace")
|
|
lines = content.splitlines()
|
|
is_test_only = file_is_test_only(path, content)
|
|
out: list[tuple[int, str, str]] = []
|
|
|
|
for line_no, line in enumerate(lines, start=1):
|
|
stripped = line.strip()
|
|
if not stripped or stripped.startswith("//"):
|
|
continue
|
|
|
|
# Rule R1 — direct stub-macro calls.
|
|
for suffix in SKIP_LINE_SUFFIXES:
|
|
if stripped.endswith(suffix) or f".{suffix}" in stripped:
|
|
if not is_test_only:
|
|
out.append(
|
|
(
|
|
line_no,
|
|
"R1",
|
|
f"`{suffix}` is a stub. Implement the case (see § 0.5).",
|
|
)
|
|
)
|
|
|
|
# Rule R2 — empty catch-all in matches.
|
|
if EMPTY_CATCH_ALL.match(line):
|
|
# Heuristic: empty body is fine in some contexts (e.g. specific
|
|
# feature flags), but only flag when there's a preceding match.
|
|
# We just flag with a low-severity tag.
|
|
out.append(
|
|
(
|
|
line_no,
|
|
"R2",
|
|
"empty catch-all match arm `_ => {}` — confirm this is intentional",
|
|
)
|
|
)
|
|
|
|
# Rule R3 — DriverParams::default() returns in production code.
|
|
if not is_test_only and DRIVER_PARAMS_DEFAULT.search(line):
|
|
out.append(
|
|
(
|
|
line_no,
|
|
"R3",
|
|
"`DriverParams::default()` is a stub for production drivers — define real params",
|
|
)
|
|
)
|
|
|
|
for pattern, rule_id, message in [STUB_MACRO_PATTERN]:
|
|
if is_test_only:
|
|
break
|
|
import re as _re
|
|
if _re.search(pattern, stripped):
|
|
out.append((line_no, rule_id, message))
|
|
|
|
return out
|
|
|
|
|
|
def main() -> int:
|
|
violations_total = 0
|
|
files_scanned = 0
|
|
files_clean = 0
|
|
|
|
for path in iter_rust_files():
|
|
files_scanned += 1
|
|
violations = audit_file(path)
|
|
if not violations:
|
|
files_clean += 1
|
|
continue
|
|
violations_total += len(violations)
|
|
for line_no, rule_id, message in violations:
|
|
rel = path.relative_to(REPO_ROOT)
|
|
print(f"{rel}:{line_no}: [{rule_id}] {message}")
|
|
|
|
print()
|
|
print(f"=== driver-manager-audit-no-stubs.py ===")
|
|
print(f"files scanned: {files_scanned}")
|
|
print(f"files clean: {files_clean}")
|
|
print(f"violations: {violations_total}")
|
|
print(f"root: {REPO_ROOT}")
|
|
print(
|
|
"rules: R1 (stub macros), R2 (empty catch-all), R3 (DriverParams::default),"
|
|
)
|
|
print(" R5 (todo!/unimplemented! in callbacks)")
|
|
if violations_total > 0:
|
|
print()
|
|
print("FAIL — driver-manager has unresolved § 0.5 comprehensive-implementation violations.")
|
|
print("Fix every violation above and re-run. See migration plan § 5.1 D4 exit criteria.")
|
|
return 1
|
|
print()
|
|
print("OK — every scanned file passes the § 0.5 stub-audit.")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|