tianole/scripts/tools/structure_checks/public_headers.py

115 lines
2.7 KiB
Python

from pathlib import Path
from .common import read_text
from .public_header_decls import (
collect_function_declarations,
collect_public_type_declarations,
symbol_name_from_type_declaration,
)
def is_documented_public_define(lines: list[str], index: int, guard: str | None) -> bool:
stripped = lines[index].strip()
if not stripped.startswith("#define "):
return False
parts = stripped.split()
if len(parts) < 2:
return False
name = parts[1].split("(", 1)[0]
if name == guard:
return False
return True
def header_guard_name(lines: list[str]) -> str | None:
for line in lines:
stripped = line.strip()
if stripped.startswith("#define "):
parts = stripped.split()
if len(parts) >= 2:
return parts[1]
return None
def has_kernel_doc_before(lines: list[str], start: int) -> bool:
index = start - 1
while index >= 0 and lines[index].strip() == "":
index -= 1
if index < 0 or lines[index].strip() != "*/":
return False
while index >= 0:
if lines[index].strip().startswith("/**"):
return True
index -= 1
return False
def has_blank_line_before_doc(lines: list[str], start: int) -> bool:
index = start - 1
while index >= 0 and lines[index].strip() == "":
index -= 1
if index < 0 or lines[index].strip() != "*/":
return True
while index >= 0 and not lines[index].strip().startswith("/**"):
index -= 1
if index <= 0:
return True
return lines[index - 1].strip() == ""
def check_kernel_doc_block(
errors: list[str], path: Path, lines: list[str], start: int, kind: str, name: str
) -> None:
line_no = start + 1
if not has_kernel_doc_before(lines, start):
errors.append(
f"{path}:{line_no}: public {kind} '{name}' needs "
"a kernel-doc comment immediately before it"
)
return
if not has_blank_line_before_doc(lines, start):
errors.append(
f"{path}:{line_no}: public {kind} '{name}' "
"comment block must be separated by a blank line"
)
def check_public_header_docs(root: Path, files: list[Path]) -> list[str]:
errors = []
for path in files:
lines = read_text(root, path).splitlines()
for start, _end, text in collect_function_declarations(lines):
name = text.split("(", 1)[0].split()[-1].lstrip("*")
check_kernel_doc_block(errors, path, lines, start, "function", name)
for start, _end, text in collect_public_type_declarations(lines):
name = symbol_name_from_type_declaration(text)
check_kernel_doc_block(errors, path, lines, start, "type", name)
guard = header_guard_name(lines)
for index, line in enumerate(lines):
if not is_documented_public_define(lines, index, guard):
continue
name = line.strip().split()[1].split("(", 1)[0]
check_kernel_doc_block(errors, path, lines, index, "macro", name)
return errors