diff options
author | Matthew Pickering <matthewtpickering@gmail.com> | 2022-02-18 17:27:32 +0100 |
---|---|---|
committer | Marge Bot <ben+marge-bot@smart-cactus.org> | 2022-02-24 20:25:06 -0500 |
commit | 6555b68ca0678827b89c5624db071f5a485d18b7 (patch) | |
tree | 9dbcd231add48a179d7751606523865029d2fc1a /.gitlab | |
parent | 06c18990fb6f10aaf1907ba8f0fe3f1a138da159 (diff) | |
download | haskell-6555b68ca0678827b89c5624db071f5a485d18b7.tar.gz |
Move linters into the tree
This MR moves the GHC linters into the tree, so that they can be run directly using Hadrian.
* Query all files tracked by Git instead of using changed files, so that we can run the exact same linting step locally and in a merge request.
* Only check that the changelogs don't contain TBA when RELEASE=YES.
* Add hadrian/lint script, which runs all the linting steps.
* Ensure the hlint job exits with a failure if hlint is not installed (otherwise we were ignoring the failure). Given that hlint doesn't seem to be available in CI at the moment, I've temporarily allowed failure in the hlint job.
* Run all linting tests in CI using hadrian.
Diffstat (limited to '.gitlab')
-rwxr-xr-x | .gitlab/linters/check-changelogs.sh | 16 | ||||
-rwxr-xr-x | .gitlab/linters/check-cpp.py | 42 | ||||
-rwxr-xr-x | .gitlab/linters/check-makefiles.py | 45 | ||||
-rwxr-xr-x | .gitlab/linters/check-version-number.sh | 7 | ||||
-rw-r--r-- | .gitlab/linters/linter.py | 130 |
5 files changed, 0 insertions, 240 deletions
diff --git a/.gitlab/linters/check-changelogs.sh b/.gitlab/linters/check-changelogs.sh deleted file mode 100755 index 56ac187a90..0000000000 --- a/.gitlab/linters/check-changelogs.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env bash - -set -e - -COLOR_RED="\e[31m" -COLOR_GREEN="\e[32m" -COLOR_NONE="\e[0m" - -grep TBA libraries/*/changelog.md && ( - echo -e "${COLOR_RED}Error: Found \"TBA\"s in changelogs.${COLOR_NONE}" - exit 1 -) || ( - echo -e "${COLOR_GREEN}changelogs look okay.${COLOR_NONE}" - exit 0 -) - diff --git a/.gitlab/linters/check-cpp.py b/.gitlab/linters/check-cpp.py deleted file mode 100755 index ffa430e10d..0000000000 --- a/.gitlab/linters/check-cpp.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/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, RegexpLinter - -linters = [ - RegexpLinter(r'WARN\s+\(', - message='CPP macros should not have a space between the macro name and their argument list'), - RegexpLinter(r'ASSERT\s+\(', - message='CPP macros should not have a space between the macro name and their argument list'), - RegexpLinter(r'ASSERT2\s+\(', - message='CPP macros should not have a space between the macro name and their argument list'), - RegexpLinter(r'#ifdef\s+', - message='`#if defined(x)` is preferred to `#ifdef x`'), - RegexpLinter(r'#if\s+defined\s+', - message='`#if defined(x)` is preferred to `#if defined x`'), - RegexpLinter(r'#ifndef\s+', - message='`#if !defined(x)` is preferred to `#ifndef x`'), -] - -for l in linters: - # Need do document rules! - l.add_path_filter(lambda path: path != Path('docs', 'coding-style.html')) - l.add_path_filter(lambda path: path != Path('docs', 'users_guide', 'utils.rst')) - # Don't lint vendored code - l.add_path_filter(lambda path: not path.name == 'config.guess') - # Don't lint files from external xxhash projects - l.add_path_filter(lambda path: path != Path('rts', 'xxhash.h')), - # Don't lint font files - l.add_path_filter(lambda path: not path.parent == Path('docs','users_guide', - 'rtd-theme', 'static', 'fonts')) - # Don't lint image files - l.add_path_filter(lambda path: not path.parent == Path('docs','users_guide', - 'images')) - # Don't lint core spec - l.add_path_filter(lambda path: not path.name == 'core-spec.pdf') - -if __name__ == '__main__': - run_linters(linters) diff --git a/.gitlab/linters/check-makefiles.py b/.gitlab/linters/check-makefiles.py deleted file mode 100755 index 5a8286c6a7..0000000000 --- a/.gitlab/linters/check-makefiles.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/env python3 - -""" -Linters for testsuite makefiles -""" - -from linter import run_linters, RegexpLinter - -""" -Warn for use of `--interactive` inside Makefiles (#11468). - -Encourage the use of `$(TEST_HC_OPTS_INTERACTIVE)` instead of -`$(TEST_HC_OPTS) --interactive -ignore-dot-ghci -v0`. It's too easy to -forget one of those flags when adding a new test. -""" -interactive_linter = \ - RegexpLinter(r'--interactive', - message = "Warning: Use `$(TEST_HC_OPTS_INTERACTIVE)` instead of `--interactive -ignore-dot-ghci -v0`." - ).add_path_filter(lambda path: path.name == 'Makefile') - -test_hc_quotes_linter = \ - RegexpLinter('\t\\$\\(TEST_HC\\)', - message = "Warning: $(TEST_HC) should be quoted in Makefiles.", - ).add_path_filter(lambda path: path.name == 'Makefile') - -ghc_pkg_quotes_linter = \ - RegexpLinter('\t\\$\\(GHC_PKG\\)', - message = "Warning: $(GHC_PKG) should be quoted in Makefiles.", - ).add_path_filter(lambda path: path.name == 'Makefile') - -haddock_quotes_linter = \ - RegexpLinter('\t\\$\\(HADDOCK\\)', - message = "Warning: $(HADDOCK) should be quoted in Makefiles.", - ).add_path_filter(lambda path: path.name == 'Makefile') - -linters = [ - interactive_linter, - test_hc_quotes_linter, - ghc_pkg_quotes_linter, - haddock_quotes_linter -] - -if __name__ == '__main__': - run_linters(linters, - subdir='testsuite') diff --git a/.gitlab/linters/check-version-number.sh b/.gitlab/linters/check-version-number.sh deleted file mode 100755 index 4f478e487a..0000000000 --- a/.gitlab/linters/check-version-number.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -set -e - -grep -E -q 'RELEASE=NO' configure.ac || - grep -E -q '\[[0-9]+\.[0-9]+\.[0-9]+\]' configure.ac || - ( echo "error: configure.ac: GHC version number must have three components when RELEASE=YES."; exit 1 ) diff --git a/.gitlab/linters/linter.py b/.gitlab/linters/linter.py deleted file mode 100644 index 61fe18de33..0000000000 --- a/.gitlab/linters/linter.py +++ /dev/null @@ -1,130 +0,0 @@ -""" -Utilities for linters -""" - -import os -import sys -import re -import textwrap -import subprocess -from pathlib import Path -from typing import List, Optional, Callable, Sequence -from collections import namedtuple - -def lint_failure(file, line_no: int, line_content: str, message: str): - """ Print a lint failure message. """ - wrapper = textwrap.TextWrapper(initial_indent=' ', - subsequent_indent=' ') - body = wrapper.fill(message) - msg = ''' - {file}: - - | - {line_no:5d} | {line_content} - | - - {body} - '''.format(file=file, line_no=line_no, - line_content=line_content, - body=body) - - print(textwrap.dedent(msg)) - -def get_changed_files(base_commit: str, head_commit: str, - subdir: str = '.'): - """ Get the files changed by the given range of commits. """ - cmd = ['git', 'diff', '--name-only', - base_commit, head_commit, '--', subdir] - files = subprocess.check_output(cmd) - return files.decode('UTF-8').split('\n') - -Warning = namedtuple('Warning', 'path,line_no,line_content,message') - -class Linter(object): - """ - A :class:`Linter` must implement :func:`lint`, which looks at the - given path and calls :func:`add_warning` for any lint issues found. - """ - def __init__(self): - self.warnings = [] # type: List[Warning] - self.path_filters = [] # type: List[Callable[[Path], bool]] - - def add_warning(self, w: Warning): - self.warnings.append(w) - - def add_path_filter(self, f: Callable[[Path], bool]) -> "Linter": - self.path_filters.append(f) - return self - - def do_lint(self, path: Path): - if all(f(path) for f in self.path_filters): - self.lint(path) - - def lint(self, path: Path): - raise NotImplementedError - -class LineLinter(Linter): - """ - A :class:`LineLinter` must implement :func:`lint_line`, which looks at - the given line from a file and calls :func:`add_warning` for any lint - issues found. - """ - def lint(self, path: Path): - if path.is_file(): - with path.open('r') as f: - for line_no, line in enumerate(f): - self.lint_line(path, line_no+1, line) - - def lint_line(self, path: Path, line_no: int, line: str): - raise NotImplementedError - -class RegexpLinter(LineLinter): - """ - A :class:`RegexpLinter` produces the given warning message for - all lines matching the given regular expression. - """ - def __init__(self, regex: str, message: str): - LineLinter.__init__(self) - self.re = re.compile(regex) - self.message = message - - def lint_line(self, path: Path, line_no: int, line: str): - if self.re.search(line): - w = Warning(path=path, line_no=line_no, line_content=line[:-1], - message=self.message) - self.add_warning(w) - -def run_linters(linters: Sequence[Linter], - subdir: str = '.') -> None: - import argparse - parser = argparse.ArgumentParser() - subparsers = parser.add_subparsers() - - subparser = subparsers.add_parser('commits', help='Lint a range of commits') - subparser.add_argument('base', help='Base commit') - subparser.add_argument('head', help='Head commit') - subparser.set_defaults(get_linted_files=lambda args: - get_changed_files(args.base, args.head, subdir)) - - subparser = subparsers.add_parser('files', help='Lint a range of commits') - subparser.add_argument('file', nargs='+', help='File to lint') - subparser.set_defaults(get_linted_files=lambda args: args.file) - - args = parser.parse_args() - - linted_files = args.get_linted_files(args) - for path in linted_files: - if path.startswith('.gitlab/linters'): - continue - for linter in linters: - linter.do_lint(Path(path)) - - warnings = [warning - for linter in linters - for warning in linter.warnings] - warnings = sorted(warnings, key=lambda x: (x.path, x.line_no)) - for w in warnings: - lint_failure(w.path, w.line_no, w.line_content, w.message) - - if len(warnings) > 0: - sys.exit(1) |