lint: Rewrite custom checks to use a more consistent framework.

This commit is contained in:
Tim Abbott 2015-12-05 13:47:50 -08:00
parent 8a18e78a65
commit a712954c59
1 changed files with 56 additions and 64 deletions

View File

@ -4,6 +4,7 @@ import re
import sys import sys
import optparse import optparse
import subprocess import subprocess
import traceback
from os import path from os import path
from collections import defaultdict from collections import defaultdict
@ -75,67 +76,6 @@ for filepath in files:
by_lang[exn].append(filepath) by_lang[exn].append(filepath)
def check_whitespace(fn):
failed = False
for i, line in enumerate(open(fn)):
if re.search('\s+$', line.strip('\n')):
sys.stdout.write('Fix whitespace at %s line %s\n' % (fn, i+1))
failed = True
if re.search('".*"%\([a-z_].*\)$', line.strip()):
sys.stdout.write('Missing space around "%%" at %s line %s\n' % (fn, i+1))
failed = True
if re.search('[)]{$', line.strip()):
sys.stdout.write('Missing space between ) and { at %s line %s\n' % (fn, i+1))
failed = True
if re.search('else{$', line.strip()):
sys.stdout.write('Missing space between else and { at %s line %s\n' % (fn, i+1))
failed = True
if re.search('\t', line):
sys.stdout.write('Fix tab-based whitespace at %s line %s\n' % (fn, i+1))
failed = True
return failed
def perform_extra_js_checks(fn):
failed = False
for i, line in enumerate(open(fn)):
line = line.strip('\n')
if re.search('[^_]function\(', line):
sys.stdout.write('The keyword "function" should be followed by a space in %s line %s\n' % (fn, i+1))
print line
failed = True
if 'blueslip.warning(' in line:
sys.stdout.write('The module blueslip has no function warning, try using blueslip.warn on line %s' % (i+1, ))
print line
failed = True
return failed
def check_python_gotchas(fn):
'''
Check for certain Python gotchas that pyflakes doesn't catch.
'''
failed = False
for i, line in enumerate(open(fn)):
line = line.strip('\n')
# Hacks to skip lines that confuse our dirt simple code:
if re.match('\s*[*#]', line):
continue
if 'help=' in line:
continue
gotcha_regexes = [
"'[^']*'\s+\([^']*$", # 'foo'(2) makes no sense
'"[^"]*"\s+\([^"]*$', # ditto
'% [a-z_]*\)?$', # "%s" % s [we prefer "%s" % (s,)]
]
for gotcha_regex in gotcha_regexes:
if re.search(gotcha_regex, line):
sys.stdout.write('Suspicious code at %s line %s (regex=%r)\n' % (fn, i+1, gotcha_regex))
print line
failed = True
break
return failed
# Invoke the appropriate lint checker for each language, # Invoke the appropriate lint checker for each language,
# and also check files for extra whitespace. # and also check files for extra whitespace.
@ -169,22 +109,74 @@ def check_pyflakes():
failed = True failed = True
return failed return failed
def custom_check_file(fn, rules, skip_rules=[]):
failed = False
for i, line in enumerate(open(fn)):
skip = False
for rule in skip_rules:
if re.match(rule, line):
skip = True
if skip:
continue
for rule in rules:
try:
if re.search(rule['pattern'], line.strip(rule.get('strip', None))):
sys.stdout.write(rule['description'] + ' at %s line %s:\n' % (fn, i+1))
print line
failed = True
except Exception:
print "Exception with %s at %s line %s" % (rule['pattern'], fn, i+1)
traceback.print_exc()
return failed
whitespace_rules = [
{'pattern': '\s+$',
'strip': '\n',
'description': 'Fix trailing whitespace'},
{'pattern': '\t',
'strip': '\n',
'description': 'Fix tab-based whitespace'},
{'pattern': '".*"%\([a-z_].*\)$',
'description': 'Missing space around "%"'},
{'pattern': '[)]{$',
'description': 'Missing space between ) and {'},
{'pattern': 'else{$',
'description': 'Missing space between else and {'},
]
js_rules = [
{'pattern': '[^_]function\(',
'description': 'The keyword "function" should be followed by a space'},
{'pattern': '.*blueslip.warning\(.*',
'description': 'The module blueslip has no function warning, try using blueslip.warn'},
]
python_rules = [
{'pattern': "'[^']*'\s+\([^']*$",
'description': "Suspicious code with quoting around function name"},
{'pattern': '"[^"]*"\s+\([^"]*$',
'description': "Suspicious code with quoting around function name"},
{'pattern': '% [a-z_]*\)?$',
'description': 'Used % comprehension without a tuple'},
]
python_line_skip_rules = [
'\s*[*#]', # comments
]
def check_custom_checks(): def check_custom_checks():
failed = False failed = False
for fn in by_lang['.py']: for fn in by_lang['.py']:
if check_python_gotchas(fn): if custom_check_file(fn, python_rules, skip_rules=python_line_skip_rules):
failed = True failed = True
for fn in by_lang['.js']: for fn in by_lang['.js']:
if perform_extra_js_checks(fn): if custom_check_file(fn, js_rules):
failed = True failed = True
whitespace_exceptions = set(['zerver/lib/bugdown/codehilite.py']) whitespace_exceptions = set(['zerver/lib/bugdown/codehilite.py'])
whitespace_targets = by_lang['.js'] + by_lang['.py'] whitespace_targets = by_lang['.js'] + by_lang['.py']
whitespace_targets = [x for x in whitespace_targets if not x in whitespace_exceptions] whitespace_targets = [x for x in whitespace_targets if not x in whitespace_exceptions]
for fn in whitespace_targets: for fn in whitespace_targets:
if check_whitespace(fn): if custom_check_file(fn, whitespace_rules):
failed = True failed = True
return failed return failed