Skip to content

Instantly share code, notes, and snippets.

@xtqqczze
Last active August 28, 2025 16:09
Show Gist options
  • Save xtqqczze/5f660920a6587e9436b676ed90efdede to your computer and use it in GitHub Desktop.
Save xtqqczze/5f660920a6587e9436b676ed90efdede to your computer and use it in GitHub Desktop.
This file was generated by ChatGPT on 2025-08-28
#!/usr/bin/env python3
"""
find_unreferenced_cs.py
Finds all *.cs files under a given directory and lists the ones
whose **filename** is not mentioned in ANY *.csproj or *.projitems in the repo.
Run from repo root:
python3 find_unreferenced_cs.py --dir src/libraries/Common/src/Interop
Options:
--repo-root . # default: current directory
--dir <path> # directory to scan for .cs files (relative to repo root)
--ignore-case # do case-insensitive matching (default is case-sensitive)
"""
from __future__ import annotations
import argparse
import sys
from pathlib import Path
def read_text_best_effort(p: Path) -> str:
"""Read text from a file with best-effort encoding fallback."""
for enc in ("utf-8", "utf-16-le", "utf-16-be", "latin-1"):
try:
return p.read_text(encoding=enc)
except Exception:
pass
return p.read_bytes().decode("utf-8", errors="ignore")
def main() -> None:
ap = argparse.ArgumentParser(description="List .cs files not mentioned in any project file.")
ap.add_argument("--repo-root", default=".", help="Path to repo root (default: .)")
ap.add_argument("--dir", required=True,
help="Path (relative to repo root) of directory to scan for .cs files.")
ap.add_argument("--ignore-case", action="store_true",
help="Use case-insensitive matching (default: case-sensitive).")
args = ap.parse_args()
root = Path(args.repo_root).resolve()
target_dir = (root / args.dir).resolve()
if not target_dir.exists():
print(f"ERROR: Target directory not found: {target_dir}", file=sys.stderr)
sys.exit(2)
# Gather all project files in repo
proj_files = list(root.rglob("*.csproj")) + list(root.rglob("*.projitems"))
if not proj_files:
print("WARNING: No .csproj or .projitems files found in repo.", file=sys.stderr)
# Read all project files into one big string for fast membership checks
chunks = []
for pf in proj_files:
try:
chunks.append(read_text_best_effort(pf))
except Exception as e:
print(f"WARNING: Could not read {pf}: {e}", file=sys.stderr)
haystack = "\n".join(chunks)
haystack_cmp = haystack if not args.ignore_case else haystack.lower()
# Find all .cs files under target_dir
cs_files = sorted(target_dir.rglob("*.cs"))
unreferenced: list[str] = []
for cf in cs_files:
name = cf.name
key = name if not args.ignore_case else name.lower()
if key not in haystack_cmp:
unreferenced.append(str(cf.relative_to(root)))
# Print results
for path in unreferenced:
print(path)
print(
f"\n# Summary: {len(unreferenced)} of {len(cs_files)} .cs files in {target_dir.relative_to(root)} "
f"not mentioned in any .csproj/.projitems",
file=sys.stderr
)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment