diff options
-rw-r--r-- | testsuite/tests/linters/Makefile | 3 | ||||
-rw-r--r-- | testsuite/tests/linters/all.T | 6 | ||||
-rwxr-xr-x | testsuite/tests/linters/regex-linters/check-rts-includes.py | 91 |
3 files changed, 99 insertions, 1 deletions
diff --git a/testsuite/tests/linters/Makefile b/testsuite/tests/linters/Makefile index 54ef4db132..2b4c2ad2c3 100644 --- a/testsuite/tests/linters/Makefile +++ b/testsuite/tests/linters/Makefile @@ -20,6 +20,9 @@ version-number: cpp: (cd $(TOP)/tests/linters/ && python3 regex-linters/check-cpp.py tracked) +rts-includes: + (cd $(TOP)/tests/linters/ && python3 regex-linters/check-rts-includes.py tracked) + changelogs: regex-linters/check-changelogs.sh $(TOP)/.. diff --git a/testsuite/tests/linters/all.T b/testsuite/tests/linters/all.T index 16700869a4..0e06df6d50 100644 --- a/testsuite/tests/linters/all.T +++ b/testsuite/tests/linters/all.T @@ -23,7 +23,11 @@ test('changelogs', [ no_deps if has_ls_files() else skip test('cpp', [ no_deps if has_ls_files() else skip , extra_files(["regex-linters"]) ] - , makefile_test, ['cpp']) + , makefile_test, ['cpp']) + +test('rts-includes', [ no_deps if has_ls_files() else skip + , extra_files(["regex-linters"]) ] + , makefile_test, ['rts-includes']) test('version-number', [ no_deps if has_ls_files() else skip , extra_files(["regex-linters"]) ] diff --git a/testsuite/tests/linters/regex-linters/check-rts-includes.py b/testsuite/tests/linters/regex-linters/check-rts-includes.py new file mode 100755 index 0000000000..14f22995b6 --- /dev/null +++ b/testsuite/tests/linters/regex-linters/check-rts-includes.py @@ -0,0 +1,91 @@ +#!/usr/bin/env python3 + +# A linter to warn for ASSERT macros which are separated from their argument +# list by a space, which Clang's CPP barfs on + +from pathlib import Path +from linter import run_linters, Linter, Warning + +from typing import List, Tuple +import re + +INCLUDE_RE = re.compile('# *include ([<"][^">]+[>"])') + +def get_includes(file: Path) -> List[Tuple[int, str]]: + txt = file.read_text() + return [ (line_no+1, m.group(1) ) + for (line_no, line) in enumerate(txt.split('\n')) + for m in [INCLUDE_RE.match(line)] + if m is not None + if m.group(1) != "rts/PosixSource.h"] + +def in_rts_dir(path: Path) -> bool: + return len(path.parts) > 0 and path.parts[0] == 'rts' + +class RtsHIncludeOrderLinter(Linter): + """ + Verify that "PosixSource.h" is always the first #include in source files to + ensure __USE_MINGW_ANSI_STDIO is defined before system headers are + #include'd. + """ + def __init__(self): + Linter.__init__(self) + self.add_path_filter(in_rts_dir) + self.add_path_filter(lambda path: path.suffix == '.c') + + def lint(self, path: Path): + # We do allow a few small headers to precede Rts.h + ALLOWED_HEADERS = { + '"ghcconfig.h"', + '"ghcplatform.h"', + } + + includes = get_includes(path) + headers = [x[1] for x in includes] + lines = path.read_text().split('\n') + + if '"PosixSource.h"' in headers: + for line_no, header in includes: + if header == '"PosixSource.h"': + break + elif header in ALLOWED_HEADERS: + continue + + self.add_warning(Warning( + path=path, + line_no=line_no, + line_content=lines[line_no-1], + message="PosixSource.h must be first header included in each file")) + +class PrivateIncludeLinter(Linter): + """ + Verify that system headers are not #include'd in <BeginPrivate.h> blocks as this + can result in very hard-to-diagnose linking errors due to hidden library functions. + """ + def __init__(self): + Linter.__init__(self) + self.add_path_filter(in_rts_dir) + self.add_path_filter(lambda path: path.suffix == '.h') + + def lint(self, path: Path): + private = False + lines = path.read_text().split('\n') + for line_no, include in get_includes(path): + if include == '"BeginPrivate.h"': + private = True + elif include == '"EndPrivate.h"': + private = False + elif private: + self.add_warning(Warning( + path=path, + line_no=line_no, + line_content=lines[line_no-1], + message='System header %s found inside of <BeginPrivate.h> block' % include)) + +linters = [ + RtsHIncludeOrderLinter(), + PrivateIncludeLinter(), +] + +if __name__ == '__main__': + run_linters(linters) |