Skip to content
Snippets Groups Projects
Commit a59a66a8 authored by Ben Gamari's avatar Ben Gamari
Browse files

testsuite: Lint RTS #includes

Verifies two important properties of #includes in the RTS:

 * That system headers don't appear inside of a `<BeginPrivate.h>` block
   as this can hide system library symbols, resulting in very
   hard-to-diagnose linker errors

 * That no headers precede `Rts.h`, ensuring that __USE_MINGW_ANSI_STDIO
   is set correctly before system headers are included.
parent 5ad143fd
No related branches found
No related tags found
No related merge requests found
......@@ -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)/..
......
......@@ -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"]) ]
......
#!/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)
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment