from __future__ import annotations
import pathlib
import re
import sys
SRC = pathlib.Path("src")
ROOT = SRC / "lib.rs"
MOD_DECL = re.compile(
r"(?P<attrs>(?:^[ \t]*#\[[^\]]*\][ \t]*\n)*)"
r"^[ \t]*(?:pub(?:\([^)]*\))?[ \t]+)?mod[ \t]+(?P<name>[A-Za-z_][A-Za-z0-9_]*)[ \t]*;",
re.M,
)
FEATURE = re.compile(r'feature[ \t]*=[ \t]*"([^"]+)"')
def guaranteed_features(attrs: str) -> set[str]:
attrs = attrs.strip()
if not attrs.startswith("#[cfg("):
return set()
body = attrs[len("#[cfg(") :].rstrip().rstrip("]").rstrip(")")
if body.startswith("any(") or body.startswith("not("):
return set()
return set(FEATURE.findall(body))
def module_file(parent: pathlib.Path, name: str) -> pathlib.Path | None:
base = parent.parent / name
for candidate in (base.with_suffix(".rs"), base / "mod.rs"):
if candidate.is_file():
return candidate
return None
def walk() -> dict[pathlib.Path, dict[str, tuple[pathlib.Path, int]]]:
if not ROOT.is_file():
sys.exit(f"error: {ROOT} not found - run from the repository root")
guaranteed: dict[pathlib.Path, dict[str, tuple[pathlib.Path, int]]] = {ROOT: {}}
queue = [ROOT]
while queue:
parent = queue.pop()
text = parent.read_text(encoding="utf-8")
for match in MOD_DECL.finditer(text):
child = module_file(parent, match.group("name"))
if child is None:
continue line = text[: match.start("attrs")].count("\n") + 1
inherited = dict(guaranteed[parent])
for feature in guaranteed_features(match.group("attrs")):
inherited.setdefault(feature, (parent, line))
existing = guaranteed.setdefault(child, {})
existing.update({k: v for k, v in inherited.items() if k not in existing})
queue.append(child)
return guaranteed
def main() -> int:
guaranteed = walk()
violations: list[tuple[pathlib.Path, int, str, tuple[pathlib.Path, int]]] = []
for path, inherited in sorted(guaranteed.items()):
if not inherited:
continue
for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
stripped = line.strip()
if not stripped.startswith("#[cfg(") or stripped.startswith("#[cfg(any("):
continue
if stripped.startswith("#[cfg(all(") or stripped.startswith("#[cfg(not("):
continue
features = FEATURE.findall(stripped)
if len(features) != 1:
continue if features[0] in inherited:
violations.append((path, lineno, stripped, inherited[features[0]]))
if not violations:
print(f"feature gates OK ({len(guaranteed)} module(s) checked)")
return 0
print("redundant feature gates:\n")
for path, lineno, gate, (declared_in, decl_line) in violations:
print(f" {path}:{lineno}: {gate}")
print(f" `{path.name}` is only reachable under this feature, "
f"guaranteed at {declared_in}:{decl_line}")
print(f"\n{len(violations)} redundant gate(s). Remove each `#[cfg(...)]`, or "
f"move it to where the condition can still vary.")
return 1
if __name__ == "__main__":
sys.exit(main())