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 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117
|
#!/usr/bin/env python3
"""Simple source code checks, e.g. trailing whitespace."""
import bisect
import re
import sys
from . import violations
RULE_TRAILING_WHITESPACE = 'whitespace.trailing'
RULE_TEXT_TRAILING_WHITESPACE = 'Trailing whitespace is not allowed.'
RULE_TODO_ONE_SPACE = 'whitespace.todo'
RULE_TEXT_TODO_ONE_SPACE= 'There should be exactly one space before TODO.'
RULE_TODO_USERNAME = 'readability.todo'
RULE_TEXT_TODO_USERNAME = 'TODO comments should look like this: "// TODO(username): Text".'
RULE_TODO_SPACE_AFTER = 'whitespace.todo'
RULE_TEXT_TODO_SPACE_AFTER = '"TODO (username):" should be followed by a space.'
RE_TODO = r'^//(\s*)TODO(\(.+?\))?:?(\s|$)?'
class WhitespaceChecker(object):
"""Performs simple white space checks."""
# TODO(holtgrew): Do not allow tabs.
def check(self, filename):
vs = []
with open(filename, 'rb') as f:
line_no = 0
for line in f:
line_no += 1
if line.rstrip() == line.rstrip('\r\n'):
continue
v = violations.SimpleRuleViolation(
RULE_TRAILING_WHITESPACE, filename, line_no,
len(line.rstrip()) + 1, RULE_TEXT_TRAILING_WHITESPACE)
vs.append(v)
return dict([(v.key(), v) for v in vs])
class SourceFile(object):
def __init__(self, name):
self.name = name
class SourceLocation(object):
def __init__(self, filename, line, column, offset):
self.file = SourceFile(filename)
self.line = line
self.column = column
self.offset = offset
def __str__(self):
return '%s:%d/%d (@%d)' % (self.file.name, self.line, self.column,
self.offset)
def __repr__(self):
return str(self)
def enumerateComments(filename):
# Read file.
with open (filename, 'rb') as f:
lines = f.readlines()
fcontents = ''.join(lines)
# Build line break index.
line_starts = [0]
for line in lines:
line_starts.append(line_starts[-1] + len(line))
#print line_starts
# Search for all comments.
pattern = re.compile(
r'//.*?$|/\*.*?\*/|\'(?:\\.|[^\\\'])*\'|"(?:\\.|[^\\"])*"',
re.DOTALL | re.MULTILINE)
for match in re.finditer(pattern, fcontents):
line_start = bisect.bisect(line_starts, match.start(0))
line_end = bisect.bisect(line_starts, match.end(0) - 1)
column_start = match.start(0) - line_starts[line_start - 1]
column_end = match.end(0) - line_starts[line_end - 1]
yield (SourceLocation(filename, line_start, column_start + 1, match.start(0)),
SourceLocation(filename, line_end, column_end + 1, match.end(0)),
match.group(0))
class CommentChecker(object):
"""Performs the checks on comments."""
def check(self, filename):
vs = []
for cstart, cend, comment in enumerateComments(filename):
if comment.startswith('//'):
# Check TODO comments.
match = re.match(RE_TODO, comment)
if match:
if len(match.group(1)) > 1:
print(comment)
v = violations.SimpleRuleViolation(
RULE_TODO_ONE_SPACE, filename, cstart.line,
cstart.column, RULE_TEXT_TODO_ONE_SPACE)
vs.append(v)
if not match.group(2):
v = violations.SimpleRuleViolation(
RULE_TODO_USERNAME, filename, cstart.line,
cstart.column, RULE_TEXT_TODO_USERNAME)
vs.append(v)
if match.group(3) != ' ' and match.group(3) != '':
v = violations.SimpleRuleViolation(
RULE_TODO_SPACE_AFTER, filename, cstart.line,
cstart.column, RULE_TEXT_TODO_SPACE_AFTER)
vs.append(v)
return dict([(v.key(), v) for v in vs])
|