12ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson#!/usr/bin/python
22ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson#
32ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson# litlint
42ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson#
52ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson# Ensure RUN commands in lit tests are free of common errors.
62ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson#
72ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson# If any errors are detected, litlint returns a nonzero exit code.
82ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson#
92ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
102ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsonimport optparse
112ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsonimport re
122ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsonimport sys
132ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
142ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson# Compile regex once for all files
152ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian HodsonrunRegex = re.compile(r'(?<!-o)(?<!%run) %t\s')
162ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
172ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsondef LintLine(s):
182ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  """ Validate a line
192ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
202ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  Args:
212ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    s: str, the line to validate
222ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
232ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  Returns:
242ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    Returns an error message and a 1-based column number if an error was
252ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    detected, otherwise (None, None).
262ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  """
272ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
282ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  # Check that RUN command can be executed with an emulator
292ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  m = runRegex.search(s)
302ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  if m:
312ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    start, end = m.span()
322ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    return ('missing %run before %t', start + 2)
332ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
342ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  # No errors
352ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  return (None, None)
362ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
372ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
382ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsondef LintFile(p):
392ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  """ Check that each RUN command can be executed with an emulator
402ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
412ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  Args:
422ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    p: str, valid path to a file
432ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
442ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  Returns:
452ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    The number of errors detected.
462ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  """
472ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  errs = 0
482ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  with open(p, 'r') as f:
492ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    for i, s in enumerate(f.readlines(), start=1):
502ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson      msg, col = LintLine(s)
512ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson      if msg != None:
522ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson        errs += 1
532ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson        errorMsg = 'litlint: {}:{}:{}: error: {}.\n{}{}\n'
542ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson        arrow = (col-1) * ' ' + '^'
552ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson        sys.stderr.write(errorMsg.format(p, i, col, msg, s, arrow))
562ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  return errs
572ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
582ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
592ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodsonif __name__ == "__main__":
602ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  # Parse args
612ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  parser = optparse.OptionParser()
622ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  parser.add_option('--filter')  # ignored
632ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  (options, filenames) = parser.parse_args()
642ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
652ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  # Lint each file
662ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  errs = 0
672ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  for p in filenames:
682ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    errs += LintFile(p)
692ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson
702ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  # If errors, return nonzero
712ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson  if errs > 0:
722ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson    sys.exit(1)
732ee91b4af4353b9e6a9d591c32fedfc58fd4ef35Ian Hodson