marioparty4/tools/doc_check.py

125 lines
3.3 KiB
Python

# Scan for undocumented functions in C files
import re
import glob
from pathlib import Path
# List of C keywords to exclude
C_KEYWORDS = {
"if",
"else",
"while",
"for",
"do",
"switch",
"case",
"default",
"break",
"continue",
"return",
"goto",
"sizeof",
"typedef",
"struct",
"union",
"enum",
"static",
"extern",
"const",
"volatile",
"inline",
"register",
"restrict",
"auto",
"void",
"int",
"char",
"float",
"double",
"long",
"short",
"signed",
"unsigned",
"bool",
"_Bool",
"_Complex",
"_Imaginary",
"alignas",
"alignof",
"atomic",
"thread_local",
"noexcept",
"static_assert",
"constexpr",
}
SKIP_FILES = [
Path("src/game/kerent.c"),
Path("src/game/font.c"),
Path("src/game/ovllist.c"),
]
# Regex to match function definitions (simple heuristic, may need further tuning)
FUNC_DEF = re.compile(
r"^[ \t]*(?:[a-zA-Z_][\w\s\*\(\),]*\*?\s+)?\*?\s*([a-zA-Z_]\w*)\s*\([^;{]*\)\s*\{",
re.MULTILINE,
)
# Regex to match Doxygen-style comment immediately before function definition
DOXYGEN_COMMENT = re.compile(
r"/\*\*.*?\*/\s*(?:[a-zA-Z_][\w\s\*\(\),]*\*?\s+)?\*?([a-zA-Z_]\w*)\s*\([^;{]*\)\s*\{",
re.DOTALL,
)
# Unlabelled variables
UNLABELLED_VARIABLE = re.compile(
r"^\s+(/\*.*\*/\s*)?(?!return)[a-zA-Z0-9]+\s+\*?\*?((var_|temp_|sp|unk)\S?\d*(_field\d*\s*:\s*)?\S?\[?\d*\]?)(\s*=\s*\S*)?;",
re.MULTILINE,
)
if __name__ == "__main__":
files = glob.glob("src/game/**/*.c", recursive=True)
total_funcs = 0
total_documented = 0
total_unlabelled = 0
print("File".ljust(31), "Doc %", "Total".ljust(10), "Unlabelled Vars")
print("-" * 64)
for file in files:
file_path = Path(file)
if file_path in SKIP_FILES:
continue
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
# Find all function definitions
all_funcs = set(match.group(1) for match in FUNC_DEF.finditer(content))
# Find all documented functions
documented_funcs = set(
match.group(1) for match in DOXYGEN_COMMENT.finditer(content)
)
# Filter out C keywords
all_funcs = {func for func in all_funcs if func not in C_KEYWORDS}
# Find all unlabeled variables
all_vars = UNLABELLED_VARIABLE.findall(content)
# Undocumented = all - documented
undocumented = all_funcs - documented_funcs
percent = (len(documented_funcs) / len(all_funcs)) * 100 if all_funcs else 0
percent_str = f"{percent:05.2f}" if percent < 10 else f"{percent:05.2f}"
total_count = f"({len(documented_funcs)}/{len(all_funcs)})".ljust(7)
print(f"{file:<30}: {percent_str}% {total_count} : {len(all_vars)}")
total_funcs += len(all_funcs)
total_documented += len(documented_funcs)
total_unlabelled += len(all_vars)
if total_funcs:
total_percent = (total_documented / total_funcs) * 100
else:
total_percent = 0
print(
f"\nTotal: {total_documented}/{total_funcs} functions documented ({total_percent:.2f}%)"
)
print(f"Total unlabelled variables: {total_unlabelled}")