Skip to content
Snippets Groups Projects
Forked from Glasgow Haskell Compiler / GHC
6830 commits behind, 437 commits ahead of the upstream repository.
  • Krzysztof Gogolewski's avatar
    14c4f701
    Documentation fixes · 14c4f701
    Krzysztof Gogolewski authored
    - Add missing :since: for NondecreasingIndentation and OverlappingInstances
    - Remove duplicated descriptions for Safe Haskell flags and
      UndecidableInstances. Instead, the sections contain a link.
    - compare-flags: Also check for options supported by ghci.
      This uncovered two more that are not documented.
      The flag -smp was removed.
    - Formatting fixes
    - Remove the warning about -XNoImplicitPrelude - it was written in 1996,
      the extension is no longer dangerous.
    - Fix misspelled :reverse: flags
    
    Fixes #18958.
    14c4f701
    History
    Documentation fixes
    Krzysztof Gogolewski authored
    - Add missing :since: for NondecreasingIndentation and OverlappingInstances
    - Remove duplicated descriptions for Safe Haskell flags and
      UndecidableInstances. Instead, the sections contain a link.
    - compare-flags: Also check for options supported by ghci.
      This uncovered two more that are not documented.
      The flag -smp was removed.
    - Formatting fixes
    - Remove the warning about -XNoImplicitPrelude - it was written in 1996,
      the extension is no longer dangerous.
    - Fix misspelled :reverse: flags
    
    Fixes #18958.
compare-flags.py 3.05 KiB
#!/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'])
    ghci_output = subprocess.check_output([ghc_path, '--interactive', '--show-options'])

    return {flag
            for flag in ghc_output.decode('UTF-8').splitlines() +
                        ghci_output.decode('UTF-8').splitlines()
            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',
                        required=True)
    parser.add_argument('--doc-flags', type=argparse.FileType(mode='r', encoding='UTF-8'),
                        help='path of ghc-flags.txt output from Sphinx',
                        required=True)
    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()