diff --git a/Meta/check-style.py b/Meta/check-style.py index 971f724d907..2ea2723ac95 100755 --- a/Meta/check-style.py +++ b/Meta/check-style.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import os +import pathlib import re import subprocess import sys @@ -46,6 +47,16 @@ GOOD_PRAGMA_ONCE_PATTERN = re.compile('(^|\\S\n\n)#pragma once(\n\n\\S.|$)') # LibC is supposed to be a system library; don't mention the directory. BAD_INCLUDE_LIBC = re.compile("# *include "](?!\\)).*$', re.M) +SYSTEM_INCLUDE_PATTERN = re.compile("^ *# *include *<([^>]+)>(?: /[*/].*)?$") +LOCAL_INCLUDE_PATTERN = re.compile('^ *# *include *"([^>]+)"(?: /[*/].*)?$') +INCLUDE_CHECK_EXCLUDES = { + "Userland/Libraries/LibCodeComprehension/Cpp/Tests/", + "Userland/Libraries/LibCpp/Tests/parser/", + "Userland/Libraries/LibCpp/Tests/preprocessor/", +} + def should_check_file(filename): if not filename.endswith('.cpp') and not filename.endswith('.h'): @@ -67,20 +78,28 @@ def find_files_here_or_argv(): return filter(should_check_file, raw_list) +def is_in_prefix_list(filename, prefix_list): + return any( + filename.startswith(prefix) for prefix in prefix_list + ) + + def run(): errors_license = [] errors_pragma_once_bad = [] errors_pragma_once_missing = [] errors_include_libc = [] + errors_include_weird_format = [] + errors_include_missing_local = [] for filename in find_files_here_or_argv(): with open(filename, "r") as f: file_content = f.read() - if not any(filename.startswith(forbidden_prefix) for forbidden_prefix in LICENSE_HEADER_CHECK_EXCLUDES): + if not is_in_prefix_list(filename, LICENSE_HEADER_CHECK_EXCLUDES): if not GOOD_LICENSE_HEADER_PATTERN.search(file_content): errors_license.append(filename) if filename.endswith('.h'): - if any(filename.startswith(forbidden_prefix) for forbidden_prefix in PRAGMA_ONCE_CHECK_EXCLUDES): + if is_in_prefix_list(filename, PRAGMA_ONCE_CHECK_EXCLUDES): # File was excluded pass elif GOOD_PRAGMA_ONCE_PATTERN.search(file_content): @@ -92,20 +111,58 @@ def run(): else: # Bad, the '#pragma once' is missing completely. errors_pragma_once_missing.append(filename) - if not any(filename.startswith(forbidden_prefix) for forbidden_prefix in LIBC_CHECK_EXCLUDES): + if not is_in_prefix_list(filename, LIBC_CHECK_EXCLUDES): if BAD_INCLUDE_LIBC.search(file_content): errors_include_libc.append(filename) + if not is_in_prefix_list(filename, INCLUDE_CHECK_EXCLUDES): + file_directory = pathlib.Path(filename).parent + for include_line in ANY_INCLUDE_PATTERN.findall(file_content): + if SYSTEM_INCLUDE_PATTERN.match(include_line): + # Don't try to resolve system-style includes, as these might depend on generators. + continue + local_match = LOCAL_INCLUDE_PATTERN.match(include_line) + if local_match is None: + print(f"Cannot parse include-line '{include_line}' in {filename}") + if filename not in errors_include_weird_format: + errors_include_weird_format.append(filename) + continue + relative_filename = local_match.group(1) + referenced_file = file_directory.joinpath(relative_filename) + if not referenced_file.exists(): + print(f"In {filename}: Cannot find {referenced_file}") + if filename not in errors_include_missing_local: + errors_include_missing_local.append(filename) + have_errors = False if errors_license: print("Files with bad licenses:", " ".join(errors_license)) + have_errors = True if errors_pragma_once_missing: print("Files without #pragma once:", " ".join(errors_pragma_once_missing)) + have_errors = True if errors_pragma_once_bad: print("Files with a bad #pragma once:", " ".join(errors_pragma_once_bad)) + have_errors = True if errors_include_libc: - print("Files that include a LibC header using #include :", " ".join(errors_include_libc)) + print( + "Files that include a LibC header using #include :", + " ".join(errors_include_libc), + ) + have_errors = True + if errors_include_weird_format: + print( + "Files that contain badly-formatted #include statements:", + " ".join(errors_include_weird_format), + ) + have_errors = True + if errors_include_missing_local: + print( + "Files that #include a missing local file:", + " ".join(errors_include_missing_local), + ) + have_errors = True - if errors_license or errors_pragma_once_missing or errors_pragma_once_bad or errors_include_libc: + if have_errors: sys.exit(1)