summaryrefslogtreecommitdiff
path: root/docs/users_guide/compare-flags.py
blob: 81a11b90e5da202fb5b8a508851ccdf9aa301e5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
Linter to verify that all flags reported by GHC's --show-options mode
are documented in the user's guide.
"""

import sys
import subprocess
from typing import Set
from pathlib import Path

# A list of known-undocumented flags. This should be considered to be a to-do
# list of flags that need to be documented.
EXPECTED_UNDOCUMENTED_PATH = \
    Path(__file__).parent / 'expected-undocumented-flags.txt'

EXPECTED_UNDOCUMENTED = \
    {line for line in EXPECTED_UNDOCUMENTED_PATH.read_text().split()}

def expected_undocumented(flag: str) -> bool:
    if flag in EXPECTED_UNDOCUMENTED:
        return True
    if flag.startswith('-Werror'):
        return True
    if flag.startswith('-Wno-') \
            or flag.startswith('-dno') \
            or flag.startswith('-fno') \
            or flag.startswith('-XNo'):
        return True
    if flag.startswith('-Wwarn=') \
            or flag.startswith('-Wno-warn='):
        return True

    return False

def read_documented_flags(doc_flags) -> Set[str]:
    # Map characters that mark the end of a flag
    # to whitespace.
    trans = str.maketrans({
        '=': ' ',
        '[': ' ',
        '⟨': ' ',
    })
    return {line.translate(trans).split()[0]
            for line in doc_flags.read().split('\n')
            if line != ''}

def read_ghc_flags(ghc_path: str) -> Set[str]:
    ghc_output = subprocess.check_output([ghc_path, '--show-options'])
    flags = {flag.strip() for flag in ghc_output.decode('UTF-8').split('\n')}
    return {flag for flag in flags
            if not expected_undocumented(flag)
            if flag != ''}

def error(s: str):
    print(s, file=sys.stderr)

def main() -> None:
    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('--ghc', type=argparse.FileType('r'),
                        help='path of GHC executable')
    parser.add_argument('--doc-flags', type=argparse.FileType(mode='r', encoding='UTF-8'),
                        help='path of ghc-flags.txt output from Sphinx')
    args = parser.parse_args()

    doc_flags = read_documented_flags(args.doc_flags)
    ghc_flags = read_ghc_flags(args.ghc.name)

    failed = False

    undocumented = ghc_flags - doc_flags
    if len(undocumented) > 0:
        error('Found {len_undoc} flags not documented in the users guide:'.format(len_undoc=len(undocumented)), )
        error('\n'.join('  {}'.format(flag) for flag in sorted(undocumented)))
        error('')
        failed = True

    now_documented = EXPECTED_UNDOCUMENTED.intersection(doc_flags)
    if len(now_documented) > 0:
        error('Found flags that are documented yet listed in {}:'.format(EXPECTED_UNDOCUMENTED_PATH))
        error('\n'.join('  {}'.format(flag) for flag in sorted(now_documented)))
        error('')
        failed = True

    if failed:
        sys.exit(1)


if __name__ == '__main__':
    main()