litlint.py revision 2d1fdb26e458c4ddc04155c1d421bced3ba90cd0
1#!/usr/bin/python
2#
3# litlint
4#
5# Ensure RUN commands in lit tests are free of common errors.
6#
7# If any errors are detected, litlint returns a nonzero exit code.
8#
9
10import optparse
11import re
12import sys
13
14# Compile regex once for all files
15runRegex = re.compile(r'(?<!-o)(?<!%run) %t\s')
16
17def LintLine(s):
18  """ Validate a line
19
20  Args:
21    s: str, the line to validate
22
23  Returns:
24    Returns an error message and a 1-based column number if an error was
25    detected, otherwise (None, None).
26  """
27
28  # Check that RUN command can be executed with an emulator
29  m = runRegex.search(s)
30  if m:
31    start, end = m.span()
32    return ('missing %run before %t', start + 2)
33
34  # No errors
35  return (None, None)
36
37
38def LintFile(p):
39  """ Check that each RUN command can be executed with an emulator
40
41  Args:
42    p: str, valid path to a file
43
44  Returns:
45    The number of errors detected.
46  """
47  errs = 0
48  with open(p, 'r') as f:
49    for i, s in enumerate(f.readlines(), start=1):
50      msg, col = LintLine(s)
51      if msg != None:
52        errs += 1
53        errorMsg = 'litlint: {}:{}:{}: error: {}.\n{}{}\n'
54        arrow = (col-1) * ' ' + '^'
55        sys.stderr.write(errorMsg.format(p, i, col, msg, s, arrow))
56  return errs
57
58
59if __name__ == "__main__":
60  # Parse args
61  parser = optparse.OptionParser()
62  parser.add_option('--filter')  # ignored
63  (options, filenames) = parser.parse_args()
64
65  # Lint each file
66  errs = 0
67  for p in filenames:
68    errs += LintFile(p)
69
70  # If errors, return nonzero
71  if errs > 0:
72    sys.exit(1)
73