mirror of
https://github.com/google/styleguide.git
synced 2024-03-22 13:11:43 +08:00
Update cpplint.py to #296:
- Check for NUL bytes in the file. - Fixed many false positives related to brace initialization. - Improved accuracy of parsing reference template parameters. - Added support for C++11 raw strings. - Added CUDA extensions to those allowed by cpplint. - Added check for incomplete namespaces. - Silence warnings for missing spaces after comma due to elided comments. - Rephrased some messages so that they're actionable. - Fix false positive on namespace qualified function pointer arguments. - Fix false positive for operators which return strings. - Revive the VLOG check. R=mark@chromium.org Review URL: https://codereview.appspot.com/17450043
This commit is contained in:
parent
c667123215
commit
2aa5998d82
550
cpplint/cpplint.py
vendored
550
cpplint/cpplint.py
vendored
@ -124,7 +124,6 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
|
|||||||
# We want an explicit list so we can list them all in cpplint --filter=.
|
# We want an explicit list so we can list them all in cpplint --filter=.
|
||||||
# If you add a new error message with a new category, add it to the list
|
# If you add a new error message with a new category, add it to the list
|
||||||
# here! cpplint_unittest.py should tell you if you forget to do this.
|
# here! cpplint_unittest.py should tell you if you forget to do this.
|
||||||
# \ used for clearer layout -- pylint: disable-msg=C6013
|
|
||||||
_ERROR_CATEGORIES = [
|
_ERROR_CATEGORIES = [
|
||||||
'build/class',
|
'build/class',
|
||||||
'build/deprecated',
|
'build/deprecated',
|
||||||
@ -151,6 +150,7 @@ _ERROR_CATEGORIES = [
|
|||||||
'readability/multiline_string',
|
'readability/multiline_string',
|
||||||
'readability/namespace',
|
'readability/namespace',
|
||||||
'readability/nolint',
|
'readability/nolint',
|
||||||
|
'readability/nul',
|
||||||
'readability/streams',
|
'readability/streams',
|
||||||
'readability/todo',
|
'readability/todo',
|
||||||
'readability/utf8',
|
'readability/utf8',
|
||||||
@ -168,15 +168,16 @@ _ERROR_CATEGORIES = [
|
|||||||
'runtime/references',
|
'runtime/references',
|
||||||
'runtime/string',
|
'runtime/string',
|
||||||
'runtime/threadsafe_fn',
|
'runtime/threadsafe_fn',
|
||||||
'whitespace/blank_line',
|
'runtime/vlog',
|
||||||
'whitespace/braces',
|
'whitespace/blank_line',
|
||||||
'whitespace/comma',
|
'whitespace/braces',
|
||||||
'whitespace/comments',
|
'whitespace/comma',
|
||||||
'whitespace/empty_conditional_body',
|
'whitespace/comments',
|
||||||
'whitespace/empty_loop_body',
|
'whitespace/empty_conditional_body',
|
||||||
'whitespace/end_of_line',
|
'whitespace/empty_loop_body',
|
||||||
'whitespace/ending_newline',
|
'whitespace/end_of_line',
|
||||||
'whitespace/forcolon',
|
'whitespace/ending_newline',
|
||||||
|
'whitespace/forcolon',
|
||||||
'whitespace/indent',
|
'whitespace/indent',
|
||||||
'whitespace/line_length',
|
'whitespace/line_length',
|
||||||
'whitespace/newline',
|
'whitespace/newline',
|
||||||
@ -547,6 +548,9 @@ class _IncludeState(dict):
|
|||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
dict.__init__(self)
|
dict.__init__(self)
|
||||||
|
self.ResetSection()
|
||||||
|
|
||||||
|
def ResetSection(self):
|
||||||
# The name of the current section.
|
# The name of the current section.
|
||||||
self._section = self._INITIAL_SECTION
|
self._section = self._INITIAL_SECTION
|
||||||
# The path of last found header.
|
# The path of last found header.
|
||||||
@ -981,7 +985,7 @@ def Error(filename, linenum, category, confidence, message):
|
|||||||
filename, linenum, message, category, confidence))
|
filename, linenum, message, category, confidence))
|
||||||
|
|
||||||
|
|
||||||
# Matches standard C++ escape esequences per 2.13.2.3 of the C++ standard.
|
# Matches standard C++ escape sequences per 2.13.2.3 of the C++ standard.
|
||||||
_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
|
_RE_PATTERN_CLEANSE_LINE_ESCAPES = re.compile(
|
||||||
r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
|
r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
|
||||||
# Matches strings. Escape codes should already be removed by ESCAPES.
|
# Matches strings. Escape codes should already be removed by ESCAPES.
|
||||||
@ -1020,6 +1024,67 @@ def IsCppString(line):
|
|||||||
return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
|
return ((line.count('"') - line.count(r'\"') - line.count("'\"'")) & 1) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def CleanseRawStrings(raw_lines):
|
||||||
|
"""Removes C++11 raw strings from lines.
|
||||||
|
|
||||||
|
Before:
|
||||||
|
static const char kData[] = R"(
|
||||||
|
multi-line string
|
||||||
|
)";
|
||||||
|
|
||||||
|
After:
|
||||||
|
static const char kData[] = ""
|
||||||
|
(replaced by blank line)
|
||||||
|
"";
|
||||||
|
|
||||||
|
Args:
|
||||||
|
raw_lines: list of raw lines.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
list of lines with C++11 raw strings replaced by empty strings.
|
||||||
|
"""
|
||||||
|
|
||||||
|
delimiter = None
|
||||||
|
lines_without_raw_strings = []
|
||||||
|
for line in raw_lines:
|
||||||
|
if delimiter:
|
||||||
|
# Inside a raw string, look for the end
|
||||||
|
end = line.find(delimiter)
|
||||||
|
if end >= 0:
|
||||||
|
# Found the end of the string, match leading space for this
|
||||||
|
# line and resume copying the original lines, and also insert
|
||||||
|
# a "" on the last line.
|
||||||
|
leading_space = Match(r'^(\s*)\S', line)
|
||||||
|
line = leading_space.group(1) + '""' + line[end + len(delimiter):]
|
||||||
|
delimiter = None
|
||||||
|
else:
|
||||||
|
# Haven't found the end yet, append a blank line.
|
||||||
|
line = ''
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Look for beginning of a raw string.
|
||||||
|
# See 2.14.15 [lex.string] for syntax.
|
||||||
|
matched = Match(r'^(.*)\b(?:R|u8R|uR|UR|LR)"([^\s\\()]*)\((.*)$', line)
|
||||||
|
if matched:
|
||||||
|
delimiter = ')' + matched.group(2) + '"'
|
||||||
|
|
||||||
|
end = matched.group(3).find(delimiter)
|
||||||
|
if end >= 0:
|
||||||
|
# Raw string ended on same line
|
||||||
|
line = (matched.group(1) + '""' +
|
||||||
|
matched.group(3)[end + len(delimiter):])
|
||||||
|
delimiter = None
|
||||||
|
else:
|
||||||
|
# Start of a multi-line raw string
|
||||||
|
line = matched.group(1) + '""'
|
||||||
|
|
||||||
|
lines_without_raw_strings.append(line)
|
||||||
|
|
||||||
|
# TODO(unknown): if delimiter is not None here, we might want to
|
||||||
|
# emit a warning for unterminated string.
|
||||||
|
return lines_without_raw_strings
|
||||||
|
|
||||||
|
|
||||||
def FindNextMultiLineCommentStart(lines, lineix):
|
def FindNextMultiLineCommentStart(lines, lineix):
|
||||||
"""Find the beginning marker for a multiline comment."""
|
"""Find the beginning marker for a multiline comment."""
|
||||||
while lineix < len(lines):
|
while lineix < len(lines):
|
||||||
@ -1094,9 +1159,11 @@ class CleansedLines(object):
|
|||||||
self.lines = []
|
self.lines = []
|
||||||
self.raw_lines = lines
|
self.raw_lines = lines
|
||||||
self.num_lines = len(lines)
|
self.num_lines = len(lines)
|
||||||
for linenum in range(len(lines)):
|
self.lines_without_raw_strings = CleanseRawStrings(lines)
|
||||||
self.lines.append(CleanseComments(lines[linenum]))
|
for linenum in range(len(self.lines_without_raw_strings)):
|
||||||
elided = self._CollapseStrings(lines[linenum])
|
self.lines.append(CleanseComments(
|
||||||
|
self.lines_without_raw_strings[linenum]))
|
||||||
|
elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
|
||||||
self.elided.append(CleanseComments(elided))
|
self.elided.append(CleanseComments(elided))
|
||||||
|
|
||||||
def NumLines(self):
|
def NumLines(self):
|
||||||
@ -1136,7 +1203,8 @@ def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
|
|||||||
endchar: expression closing character.
|
endchar: expression closing character.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Index just after endchar.
|
On finding matching endchar: (index just after matching endchar, 0)
|
||||||
|
Otherwise: (-1, new depth at end of this line)
|
||||||
"""
|
"""
|
||||||
for i in xrange(startpos, len(line)):
|
for i in xrange(startpos, len(line)):
|
||||||
if line[i] == startchar:
|
if line[i] == startchar:
|
||||||
@ -1144,14 +1212,14 @@ def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
|
|||||||
elif line[i] == endchar:
|
elif line[i] == endchar:
|
||||||
depth -= 1
|
depth -= 1
|
||||||
if depth == 0:
|
if depth == 0:
|
||||||
return i + 1
|
return (i + 1, 0)
|
||||||
return -1
|
return (-1, depth)
|
||||||
|
|
||||||
|
|
||||||
def CloseExpression(clean_lines, linenum, pos):
|
def CloseExpression(clean_lines, linenum, pos):
|
||||||
"""If input points to ( or { or [, finds the position that closes it.
|
"""If input points to ( or { or [ or <, finds the position that closes it.
|
||||||
|
|
||||||
If lines[linenum][pos] points to a '(' or '{' or '[', finds the
|
If lines[linenum][pos] points to a '(' or '{' or '[' or '<', finds the
|
||||||
linenum/pos that correspond to the closing of the expression.
|
linenum/pos that correspond to the closing of the expression.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -1168,30 +1236,104 @@ def CloseExpression(clean_lines, linenum, pos):
|
|||||||
|
|
||||||
line = clean_lines.elided[linenum]
|
line = clean_lines.elided[linenum]
|
||||||
startchar = line[pos]
|
startchar = line[pos]
|
||||||
if startchar not in '({[':
|
if startchar not in '({[<':
|
||||||
return (line, clean_lines.NumLines(), -1)
|
return (line, clean_lines.NumLines(), -1)
|
||||||
if startchar == '(': endchar = ')'
|
if startchar == '(': endchar = ')'
|
||||||
if startchar == '[': endchar = ']'
|
if startchar == '[': endchar = ']'
|
||||||
if startchar == '{': endchar = '}'
|
if startchar == '{': endchar = '}'
|
||||||
|
if startchar == '<': endchar = '>'
|
||||||
|
|
||||||
# Check first line
|
# Check first line
|
||||||
end_pos = FindEndOfExpressionInLine(line, pos, 0, startchar, endchar)
|
(end_pos, num_open) = FindEndOfExpressionInLine(
|
||||||
|
line, pos, 0, startchar, endchar)
|
||||||
if end_pos > -1:
|
if end_pos > -1:
|
||||||
return (line, linenum, end_pos)
|
return (line, linenum, end_pos)
|
||||||
tail = line[pos:]
|
|
||||||
num_open = tail.count(startchar) - tail.count(endchar)
|
# Continue scanning forward
|
||||||
while linenum < clean_lines.NumLines() - 1:
|
while linenum < clean_lines.NumLines() - 1:
|
||||||
linenum += 1
|
linenum += 1
|
||||||
line = clean_lines.elided[linenum]
|
line = clean_lines.elided[linenum]
|
||||||
delta = line.count(startchar) - line.count(endchar)
|
(end_pos, num_open) = FindEndOfExpressionInLine(
|
||||||
if num_open + delta <= 0:
|
line, 0, num_open, startchar, endchar)
|
||||||
return (line, linenum,
|
if end_pos > -1:
|
||||||
FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar))
|
return (line, linenum, end_pos)
|
||||||
num_open += delta
|
|
||||||
|
|
||||||
# Did not find endchar before end of file, give up
|
# Did not find endchar before end of file, give up
|
||||||
return (line, clean_lines.NumLines(), -1)
|
return (line, clean_lines.NumLines(), -1)
|
||||||
|
|
||||||
|
|
||||||
|
def FindStartOfExpressionInLine(line, endpos, depth, startchar, endchar):
|
||||||
|
"""Find position at the matching startchar.
|
||||||
|
|
||||||
|
This is almost the reverse of FindEndOfExpressionInLine, but note
|
||||||
|
that the input position and returned position differs by 1.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
line: a CleansedLines line.
|
||||||
|
endpos: start searching at this position.
|
||||||
|
depth: nesting level at endpos.
|
||||||
|
startchar: expression opening character.
|
||||||
|
endchar: expression closing character.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
On finding matching startchar: (index at matching startchar, 0)
|
||||||
|
Otherwise: (-1, new depth at beginning of this line)
|
||||||
|
"""
|
||||||
|
for i in xrange(endpos, -1, -1):
|
||||||
|
if line[i] == endchar:
|
||||||
|
depth += 1
|
||||||
|
elif line[i] == startchar:
|
||||||
|
depth -= 1
|
||||||
|
if depth == 0:
|
||||||
|
return (i, 0)
|
||||||
|
return (-1, depth)
|
||||||
|
|
||||||
|
|
||||||
|
def ReverseCloseExpression(clean_lines, linenum, pos):
|
||||||
|
"""If input points to ) or } or ] or >, finds the position that opens it.
|
||||||
|
|
||||||
|
If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the
|
||||||
|
linenum/pos that correspond to the opening of the expression.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
clean_lines: A CleansedLines instance containing the file.
|
||||||
|
linenum: The number of the line to check.
|
||||||
|
pos: A position on the line.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple (line, linenum, pos) pointer *at* the opening brace, or
|
||||||
|
(line, 0, -1) if we never find the matching opening brace. Note
|
||||||
|
we ignore strings and comments when matching; and the line we
|
||||||
|
return is the 'cleansed' line at linenum.
|
||||||
|
"""
|
||||||
|
line = clean_lines.elided[linenum]
|
||||||
|
endchar = line[pos]
|
||||||
|
if endchar not in ')}]>':
|
||||||
|
return (line, 0, -1)
|
||||||
|
if endchar == ')': startchar = '('
|
||||||
|
if endchar == ']': startchar = '['
|
||||||
|
if endchar == '}': startchar = '{'
|
||||||
|
if endchar == '>': startchar = '<'
|
||||||
|
|
||||||
|
# Check last line
|
||||||
|
(start_pos, num_open) = FindStartOfExpressionInLine(
|
||||||
|
line, pos, 0, startchar, endchar)
|
||||||
|
if start_pos > -1:
|
||||||
|
return (line, linenum, start_pos)
|
||||||
|
|
||||||
|
# Continue scanning backward
|
||||||
|
while linenum > 0:
|
||||||
|
linenum -= 1
|
||||||
|
line = clean_lines.elided[linenum]
|
||||||
|
(start_pos, num_open) = FindStartOfExpressionInLine(
|
||||||
|
line, len(line) - 1, num_open, startchar, endchar)
|
||||||
|
if start_pos > -1:
|
||||||
|
return (line, linenum, start_pos)
|
||||||
|
|
||||||
|
# Did not find startchar before beginning of file, give up
|
||||||
|
return (line, 0, -1)
|
||||||
|
|
||||||
|
|
||||||
def CheckForCopyright(filename, lines, error):
|
def CheckForCopyright(filename, lines, error):
|
||||||
"""Logs an error if no Copyright message appears at the top of the file."""
|
"""Logs an error if no Copyright message appears at the top of the file."""
|
||||||
|
|
||||||
@ -1304,13 +1446,17 @@ def CheckForHeaderGuard(filename, lines, error):
|
|||||||
'#endif line should be "#endif // %s"' % cppvar)
|
'#endif line should be "#endif // %s"' % cppvar)
|
||||||
|
|
||||||
|
|
||||||
def CheckForUnicodeReplacementCharacters(filename, lines, error):
|
def CheckForBadCharacters(filename, lines, error):
|
||||||
"""Logs an error for each line containing Unicode replacement characters.
|
"""Logs an error for each line containing bad characters.
|
||||||
|
|
||||||
These indicate that either the file contained invalid UTF-8 (likely)
|
Two kinds of bad characters:
|
||||||
or Unicode replacement characters (which it shouldn't). Note that
|
|
||||||
it's possible for this to throw off line numbering if the invalid
|
1. Unicode replacement characters: These indicate that either the file
|
||||||
UTF-8 occurred adjacent to a newline.
|
contained invalid UTF-8 (likely) or Unicode replacement characters (which
|
||||||
|
it shouldn't). Note that it's possible for this to throw off line
|
||||||
|
numbering if the invalid UTF-8 occurred adjacent to a newline.
|
||||||
|
|
||||||
|
2. NUL bytes. These are problematic for some tools.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
filename: The name of the current file.
|
filename: The name of the current file.
|
||||||
@ -1321,6 +1467,8 @@ def CheckForUnicodeReplacementCharacters(filename, lines, error):
|
|||||||
if u'\ufffd' in line:
|
if u'\ufffd' in line:
|
||||||
error(filename, linenum, 'readability/utf8', 5,
|
error(filename, linenum, 'readability/utf8', 5,
|
||||||
'Line contains invalid UTF-8 (or Unicode replacement character).')
|
'Line contains invalid UTF-8 (or Unicode replacement character).')
|
||||||
|
if '\0' in line:
|
||||||
|
error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
|
||||||
|
|
||||||
|
|
||||||
def CheckForNewlineAtEOF(filename, lines, error):
|
def CheckForNewlineAtEOF(filename, lines, error):
|
||||||
@ -1375,8 +1523,8 @@ def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
|
|||||||
if (line.count('"') - line.count('\\"')) % 2:
|
if (line.count('"') - line.count('\\"')) % 2:
|
||||||
error(filename, linenum, 'readability/multiline_string', 5,
|
error(filename, linenum, 'readability/multiline_string', 5,
|
||||||
'Multi-line string ("...") found. This lint script doesn\'t '
|
'Multi-line string ("...") found. This lint script doesn\'t '
|
||||||
'do well with such strings, and may give bogus warnings. They\'re '
|
'do well with such strings, and may give bogus warnings. '
|
||||||
'ugly and unnecessary, and you should use concatenation instead".')
|
'Use C++11 raw strings or concatenation instead.')
|
||||||
|
|
||||||
|
|
||||||
threading_list = (
|
threading_list = (
|
||||||
@ -1413,7 +1561,7 @@ def CheckPosixThreading(filename, clean_lines, linenum, error):
|
|||||||
line = clean_lines.elided[linenum]
|
line = clean_lines.elided[linenum]
|
||||||
for single_thread_function, multithread_safe_function in threading_list:
|
for single_thread_function, multithread_safe_function in threading_list:
|
||||||
ix = line.find(single_thread_function)
|
ix = line.find(single_thread_function)
|
||||||
# Comparisons made explicit for clarity -- pylint: disable-msg=C6403
|
# Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
|
||||||
if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and
|
if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and
|
||||||
line[ix - 1] not in ('_', '.', '>'))):
|
line[ix - 1] not in ('_', '.', '>'))):
|
||||||
error(filename, linenum, 'runtime/threadsafe_fn', 2,
|
error(filename, linenum, 'runtime/threadsafe_fn', 2,
|
||||||
@ -1422,6 +1570,25 @@ def CheckPosixThreading(filename, clean_lines, linenum, error):
|
|||||||
'...) for improved thread safety.')
|
'...) for improved thread safety.')
|
||||||
|
|
||||||
|
|
||||||
|
def CheckVlogArguments(filename, clean_lines, linenum, error):
|
||||||
|
"""Checks that VLOG() is only used for defining a logging level.
|
||||||
|
|
||||||
|
For example, VLOG(2) is correct. VLOG(INFO), VLOG(WARNING), VLOG(ERROR), and
|
||||||
|
VLOG(FATAL) are not.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filename: The name of the current file.
|
||||||
|
clean_lines: A CleansedLines instance containing the file.
|
||||||
|
linenum: The number of the line to check.
|
||||||
|
error: The function to call with any errors found.
|
||||||
|
"""
|
||||||
|
line = clean_lines.elided[linenum]
|
||||||
|
if Search(r'\bVLOG\((INFO|ERROR|WARNING|DFATAL|FATAL)\)', line):
|
||||||
|
error(filename, linenum, 'runtime/vlog', 5,
|
||||||
|
'VLOG() should be used with numeric verbosity level. '
|
||||||
|
'Use LOG() if you want symbolic severity levels.')
|
||||||
|
|
||||||
|
|
||||||
# Matches invalid increment: *count++, which moves pointer instead of
|
# Matches invalid increment: *count++, which moves pointer instead of
|
||||||
# incrementing a value.
|
# incrementing a value.
|
||||||
_RE_PATTERN_INVALID_INCREMENT = re.compile(
|
_RE_PATTERN_INVALID_INCREMENT = re.compile(
|
||||||
@ -1867,8 +2034,8 @@ class _NestingState(object):
|
|||||||
return classinfo
|
return classinfo
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def CheckClassFinished(self, filename, error):
|
def CheckCompletedBlocks(self, filename, error):
|
||||||
"""Checks that all classes have been completely parsed.
|
"""Checks that all classes and namespaces have been completely parsed.
|
||||||
|
|
||||||
Call this when all lines in a file have been processed.
|
Call this when all lines in a file have been processed.
|
||||||
Args:
|
Args:
|
||||||
@ -1883,11 +2050,15 @@ class _NestingState(object):
|
|||||||
error(filename, obj.starting_linenum, 'build/class', 5,
|
error(filename, obj.starting_linenum, 'build/class', 5,
|
||||||
'Failed to find complete declaration of class %s' %
|
'Failed to find complete declaration of class %s' %
|
||||||
obj.name)
|
obj.name)
|
||||||
|
elif isinstance(obj, _NamespaceInfo):
|
||||||
|
error(filename, obj.starting_linenum, 'build/namespaces', 5,
|
||||||
|
'Failed to find complete declaration of namespace %s' %
|
||||||
|
obj.name)
|
||||||
|
|
||||||
|
|
||||||
def CheckForNonStandardConstructs(filename, clean_lines, linenum,
|
def CheckForNonStandardConstructs(filename, clean_lines, linenum,
|
||||||
nesting_state, error):
|
nesting_state, error):
|
||||||
"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
|
r"""Logs an error if we see certain non-ANSI constructs ignored by gcc-2.
|
||||||
|
|
||||||
Complain about several constructs which gcc-2 accepts, but which are
|
Complain about several constructs which gcc-2 accepts, but which are
|
||||||
not standard C++. Warning about these in lint is one way to ease the
|
not standard C++. Warning about these in lint is one way to ease the
|
||||||
@ -2030,7 +2201,7 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
|||||||
# Note that we assume the contents of [] to be short enough that
|
# Note that we assume the contents of [] to be short enough that
|
||||||
# they'll never need to wrap.
|
# they'll never need to wrap.
|
||||||
if ( # Ignore control structures.
|
if ( # Ignore control structures.
|
||||||
not Search(r'\b(if|for|while|switch|return|new|delete|catch)\b',
|
not Search(r'\b(if|for|while|switch|return|new|delete|catch|sizeof)\b',
|
||||||
fncall) and
|
fncall) and
|
||||||
# Ignore pointers/references to functions.
|
# Ignore pointers/references to functions.
|
||||||
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
|
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
|
||||||
@ -2044,7 +2215,7 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
|||||||
'Extra space after (')
|
'Extra space after (')
|
||||||
if (Search(r'\w\s+\(', fncall) and
|
if (Search(r'\w\s+\(', fncall) and
|
||||||
not Search(r'#\s*define|typedef', fncall) and
|
not Search(r'#\s*define|typedef', fncall) and
|
||||||
not Search(r'\w\s+\((\w+::)?\*\w+\)\(', fncall)):
|
not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)):
|
||||||
error(filename, linenum, 'whitespace/parens', 4,
|
error(filename, linenum, 'whitespace/parens', 4,
|
||||||
'Extra space before ( in function call')
|
'Extra space before ( in function call')
|
||||||
# If the ) is followed only by a newline or a { + newline, assume it's
|
# If the ) is followed only by a newline or a { + newline, assume it's
|
||||||
@ -2172,7 +2343,7 @@ def CheckComment(comment, filename, linenum, error):
|
|||||||
'"// TODO(my_username): Stuff."')
|
'"// TODO(my_username): Stuff."')
|
||||||
|
|
||||||
middle_whitespace = match.group(3)
|
middle_whitespace = match.group(3)
|
||||||
# Comparisons made explicit for correctness -- pylint: disable-msg=C6403
|
# Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
|
||||||
if middle_whitespace != ' ' and middle_whitespace != '':
|
if middle_whitespace != ' ' and middle_whitespace != '':
|
||||||
error(filename, linenum, 'whitespace/todo', 2,
|
error(filename, linenum, 'whitespace/todo', 2,
|
||||||
'TODO(my_username) should be followed by a space')
|
'TODO(my_username) should be followed by a space')
|
||||||
@ -2352,7 +2523,10 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
error: The function to call with any errors found.
|
error: The function to call with any errors found.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raw = clean_lines.raw_lines
|
# Don't use "elided" lines here, otherwise we can't check commented lines.
|
||||||
|
# Don't want to use "raw" either, because we don't want to check inside C++11
|
||||||
|
# raw strings,
|
||||||
|
raw = clean_lines.lines_without_raw_strings
|
||||||
line = raw[linenum]
|
line = raw[linenum]
|
||||||
|
|
||||||
# Before nixing comments, check if the line is blank for no good
|
# Before nixing comments, check if the line is blank for no good
|
||||||
@ -2406,7 +2580,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
|
|
||||||
if not exception:
|
if not exception:
|
||||||
error(filename, linenum, 'whitespace/blank_line', 2,
|
error(filename, linenum, 'whitespace/blank_line', 2,
|
||||||
'Blank line at the start of a code block. Is this needed?')
|
'Redundant blank line at the start of a code block '
|
||||||
|
'should be deleted.')
|
||||||
# Ignore blank lines at the end of a block in a long if-else
|
# Ignore blank lines at the end of a block in a long if-else
|
||||||
# chain, like this:
|
# chain, like this:
|
||||||
# if (condition1) {
|
# if (condition1) {
|
||||||
@ -2421,7 +2596,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
and Match(r'\s*}', next_line)
|
and Match(r'\s*}', next_line)
|
||||||
and next_line.find('} else ') == -1):
|
and next_line.find('} else ') == -1):
|
||||||
error(filename, linenum, 'whitespace/blank_line', 3,
|
error(filename, linenum, 'whitespace/blank_line', 3,
|
||||||
'Blank line at the end of a code block. Is this needed?')
|
'Redundant blank line at the end of a code block '
|
||||||
|
'should be deleted.')
|
||||||
|
|
||||||
matched = Match(r'\s*(public|protected|private):', prev_line)
|
matched = Match(r'\s*(public|protected|private):', prev_line)
|
||||||
if matched:
|
if matched:
|
||||||
@ -2432,7 +2608,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
commentpos = line.find('//')
|
commentpos = line.find('//')
|
||||||
if commentpos != -1:
|
if commentpos != -1:
|
||||||
# Check if the // may be in quotes. If so, ignore it
|
# Check if the // may be in quotes. If so, ignore it
|
||||||
# Comparisons made explicit for clarity -- pylint: disable-msg=C6403
|
# Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
|
||||||
if (line.count('"', 0, commentpos) -
|
if (line.count('"', 0, commentpos) -
|
||||||
line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
|
line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
|
||||||
# Allow one space for new scopes, two spaces otherwise:
|
# Allow one space for new scopes, two spaces otherwise:
|
||||||
@ -2570,7 +2746,12 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
# This does not apply when the non-space character following the
|
# This does not apply when the non-space character following the
|
||||||
# comma is another comma, since the only time when that happens is
|
# comma is another comma, since the only time when that happens is
|
||||||
# for empty macro arguments.
|
# for empty macro arguments.
|
||||||
if Search(r',[^,\s]', line):
|
#
|
||||||
|
# We run this check in two passes: first pass on elided lines to
|
||||||
|
# verify that lines contain missing whitespaces, second pass on raw
|
||||||
|
# lines to confirm that those missing whitespaces are not due to
|
||||||
|
# elided comments.
|
||||||
|
if Search(r',[^,\s]', line) and Search(r',[^,\s]', raw[linenum]):
|
||||||
error(filename, linenum, 'whitespace/comma', 3,
|
error(filename, linenum, 'whitespace/comma', 3,
|
||||||
'Missing space after ,')
|
'Missing space after ,')
|
||||||
|
|
||||||
@ -2589,9 +2770,45 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||||||
# an initializer list, for instance), you should have spaces before your
|
# an initializer list, for instance), you should have spaces before your
|
||||||
# braces. And since you should never have braces at the beginning of a line,
|
# braces. And since you should never have braces at the beginning of a line,
|
||||||
# this is an easy test.
|
# this is an easy test.
|
||||||
if Search(r'[^ ({]{', line):
|
match = Match(r'^(.*[^ ({]){', line)
|
||||||
error(filename, linenum, 'whitespace/braces', 5,
|
if match:
|
||||||
'Missing space before {')
|
# Try a bit harder to check for brace initialization. This
|
||||||
|
# happens in one of the following forms:
|
||||||
|
# Constructor() : initializer_list_{} { ... }
|
||||||
|
# Constructor{}.MemberFunction()
|
||||||
|
# Type variable{};
|
||||||
|
# FunctionCall(type{}, ...);
|
||||||
|
# LastArgument(..., type{});
|
||||||
|
# LOG(INFO) << type{} << " ...";
|
||||||
|
# map_of_type[{...}] = ...;
|
||||||
|
#
|
||||||
|
# We check for the character following the closing brace, and
|
||||||
|
# silence the warning if it's one of those listed above, i.e.
|
||||||
|
# "{.;,)<]".
|
||||||
|
#
|
||||||
|
# To account for nested initializer list, we allow any number of
|
||||||
|
# closing braces up to "{;,)<". We can't simply silence the
|
||||||
|
# warning on first sight of closing brace, because that would
|
||||||
|
# cause false negatives for things that are not initializer lists.
|
||||||
|
# Silence this: But not this:
|
||||||
|
# Outer{ if (...) {
|
||||||
|
# Inner{...} if (...){ // Missing space before {
|
||||||
|
# }; }
|
||||||
|
#
|
||||||
|
# There is a false negative with this approach if people inserted
|
||||||
|
# spurious semicolons, e.g. "if (cond){};", but we will catch the
|
||||||
|
# spurious semicolon with a separate check.
|
||||||
|
(endline, endlinenum, endpos) = CloseExpression(
|
||||||
|
clean_lines, linenum, len(match.group(1)))
|
||||||
|
trailing_text = ''
|
||||||
|
if endpos > -1:
|
||||||
|
trailing_text = endline[endpos:]
|
||||||
|
for offset in xrange(endlinenum + 1,
|
||||||
|
min(endlinenum + 3, clean_lines.NumLines() - 1)):
|
||||||
|
trailing_text += clean_lines.elided[offset]
|
||||||
|
if not Match(r'^[\s}]*[{.;,)<\]]', trailing_text):
|
||||||
|
error(filename, linenum, 'whitespace/braces', 5,
|
||||||
|
'Missing space before {')
|
||||||
|
|
||||||
# Make sure '} else {' has spaces.
|
# Make sure '} else {' has spaces.
|
||||||
if Search(r'}else', line):
|
if Search(r'}else', line):
|
||||||
@ -2719,15 +2936,15 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
|||||||
line = clean_lines.elided[linenum] # get rid of comments and strings
|
line = clean_lines.elided[linenum] # get rid of comments and strings
|
||||||
|
|
||||||
if Match(r'\s*{\s*$', line):
|
if Match(r'\s*{\s*$', line):
|
||||||
# We allow an open brace to start a line in the case where someone
|
# We allow an open brace to start a line in the case where someone is using
|
||||||
# is using braces in a block to explicitly create a new scope,
|
# braces in a block to explicitly create a new scope, which is commonly used
|
||||||
# which is commonly used to control the lifetime of
|
# to control the lifetime of stack-allocated variables. Braces are also
|
||||||
# stack-allocated variables. We don't detect this perfectly: we
|
# used for brace initializers inside function calls. We don't detect this
|
||||||
# just don't complain if the last non-whitespace character on the
|
# perfectly: we just don't complain if the last non-whitespace character on
|
||||||
# previous non-blank line is ',', ';', ':', '{', or '}', or if the
|
# the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
|
||||||
# previous line starts a preprocessor block.
|
# previous line starts a preprocessor block.
|
||||||
prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
|
prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
|
||||||
if (not Search(r'[,;:}{]\s*$', prevline) and
|
if (not Search(r'[,;:}{(]\s*$', prevline) and
|
||||||
not Match(r'\s*#', prevline)):
|
not Match(r'\s*#', prevline)):
|
||||||
error(filename, linenum, 'whitespace/braces', 4,
|
error(filename, linenum, 'whitespace/braces', 4,
|
||||||
'{ should almost always be at the end of the previous line')
|
'{ should almost always be at the end of the previous line')
|
||||||
@ -2765,21 +2982,119 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
|||||||
error(filename, linenum, 'whitespace/newline', 4,
|
error(filename, linenum, 'whitespace/newline', 4,
|
||||||
'do/while clauses should not be on a single line')
|
'do/while clauses should not be on a single line')
|
||||||
|
|
||||||
# Braces shouldn't be followed by a ; unless they're defining a struct
|
# Block bodies should not be followed by a semicolon. Due to C++11
|
||||||
# or initializing an array.
|
# brace initialization, there are more places where semicolons are
|
||||||
# We can't tell in general, but we can for some common cases.
|
# required than not, so we use a whitelist approach to check these
|
||||||
prevlinenum = linenum
|
# rather than a blacklist. These are the places where "};" should
|
||||||
while True:
|
# be replaced by just "}":
|
||||||
(prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum)
|
# 1. Some flavor of block following closing parenthesis:
|
||||||
if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'):
|
# for (;;) {};
|
||||||
line = prevline + line
|
# while (...) {};
|
||||||
else:
|
# switch (...) {};
|
||||||
break
|
# Function(...) {};
|
||||||
if (Search(r'{.*}\s*;', line) and
|
# if (...) {};
|
||||||
line.count('{') == line.count('}') and
|
# if (...) else if (...) {};
|
||||||
not Search(r'struct|union|class|enum|\s*=\s*{', line)):
|
#
|
||||||
error(filename, linenum, 'readability/braces', 4,
|
# 2. else block:
|
||||||
"You don't need a ; after a }")
|
# if (...) else {};
|
||||||
|
#
|
||||||
|
# 3. const member function:
|
||||||
|
# Function(...) const {};
|
||||||
|
#
|
||||||
|
# 4. Block following some statement:
|
||||||
|
# x = 42;
|
||||||
|
# {};
|
||||||
|
#
|
||||||
|
# 5. Block at the beginning of a function:
|
||||||
|
# Function(...) {
|
||||||
|
# {};
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Note that naively checking for the preceding "{" will also match
|
||||||
|
# braces inside multi-dimensional arrays, but this is fine since
|
||||||
|
# that expression will not contain semicolons.
|
||||||
|
#
|
||||||
|
# 6. Block following another block:
|
||||||
|
# while (true) {}
|
||||||
|
# {};
|
||||||
|
#
|
||||||
|
# 7. End of namespaces:
|
||||||
|
# namespace {};
|
||||||
|
#
|
||||||
|
# These semicolons seems far more common than other kinds of
|
||||||
|
# redundant semicolons, possibly due to people converting classes
|
||||||
|
# to namespaces. For now we do not warn for this case.
|
||||||
|
#
|
||||||
|
# Try matching case 1 first.
|
||||||
|
match = Match(r'^(.*\)\s*)\{', line)
|
||||||
|
if match:
|
||||||
|
# Matched closing parenthesis (case 1). Check the token before the
|
||||||
|
# matching opening parenthesis, and don't warn if it looks like a
|
||||||
|
# macro. This avoids these false positives:
|
||||||
|
# - macro that defines a base class
|
||||||
|
# - multi-line macro that defines a base class
|
||||||
|
# - macro that defines the whole class-head
|
||||||
|
#
|
||||||
|
# But we still issue warnings for macros that we know are safe to
|
||||||
|
# warn, specifically:
|
||||||
|
# - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P
|
||||||
|
# - TYPED_TEST
|
||||||
|
# - INTERFACE_DEF
|
||||||
|
# - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED:
|
||||||
|
#
|
||||||
|
# We implement a whitelist of safe macros instead of a blacklist of
|
||||||
|
# unsafe macros, even though the latter appears less frequently in
|
||||||
|
# google code and would have been easier to implement. This is because
|
||||||
|
# the downside for getting the whitelist wrong means some extra
|
||||||
|
# semicolons, while the downside for getting the blacklist wrong
|
||||||
|
# would result in compile errors.
|
||||||
|
#
|
||||||
|
# In addition to macros, we also don't want to warn on compound
|
||||||
|
# literals.
|
||||||
|
closing_brace_pos = match.group(1).rfind(')')
|
||||||
|
opening_parenthesis = ReverseCloseExpression(
|
||||||
|
clean_lines, linenum, closing_brace_pos)
|
||||||
|
if opening_parenthesis[2] > -1:
|
||||||
|
line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]]
|
||||||
|
macro = Search(r'\b([A-Z_]+)\s*$', line_prefix)
|
||||||
|
if ((macro and
|
||||||
|
macro.group(1) not in (
|
||||||
|
'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST',
|
||||||
|
'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED',
|
||||||
|
'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or
|
||||||
|
Search(r'\s+=\s*$', line_prefix)):
|
||||||
|
match = None
|
||||||
|
|
||||||
|
else:
|
||||||
|
# Try matching cases 2-3.
|
||||||
|
match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line)
|
||||||
|
if not match:
|
||||||
|
# Try matching cases 4-6. These are always matched on separate lines.
|
||||||
|
#
|
||||||
|
# Note that we can't simply concatenate the previous line to the
|
||||||
|
# current line and do a single match, otherwise we may output
|
||||||
|
# duplicate warnings for the blank line case:
|
||||||
|
# if (cond) {
|
||||||
|
# // blank line
|
||||||
|
# }
|
||||||
|
prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0]
|
||||||
|
if prevline and Search(r'[;{}]\s*$', prevline):
|
||||||
|
match = Match(r'^(\s*)\{', line)
|
||||||
|
|
||||||
|
# Check matching closing brace
|
||||||
|
if match:
|
||||||
|
(endline, endlinenum, endpos) = CloseExpression(
|
||||||
|
clean_lines, linenum, len(match.group(1)))
|
||||||
|
if endpos > -1 and Match(r'^\s*;', endline[endpos:]):
|
||||||
|
# Current {} pair is eligible for semicolon check, and we have found
|
||||||
|
# the redundant semicolon, output warning here.
|
||||||
|
#
|
||||||
|
# Note: because we are scanning forward for opening braces, and
|
||||||
|
# outputting warnings for the matching closing brace, if there are
|
||||||
|
# nested blocks with trailing semicolons, we will get the error
|
||||||
|
# messages in reversed order.
|
||||||
|
error(filename, endlinenum, 'readability/braces', 4,
|
||||||
|
"You don't need a ; after a }")
|
||||||
|
|
||||||
|
|
||||||
def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
|
def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
|
||||||
@ -2876,7 +3191,7 @@ def CheckCheck(filename, clean_lines, linenum, error):
|
|||||||
if token == '(':
|
if token == '(':
|
||||||
# Parenthesized operand
|
# Parenthesized operand
|
||||||
expression = matched.group(2)
|
expression = matched.group(2)
|
||||||
end = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
|
(end, _) = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
|
||||||
if end < 0:
|
if end < 0:
|
||||||
return # Unmatched parenthesis
|
return # Unmatched parenthesis
|
||||||
lhs += '(' + expression[0:end]
|
lhs += '(' + expression[0:end]
|
||||||
@ -3016,7 +3331,10 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
|
|||||||
error: The function to call with any errors found.
|
error: The function to call with any errors found.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raw_lines = clean_lines.raw_lines
|
# Don't use "elided" lines here, otherwise we can't check commented lines.
|
||||||
|
# Don't want to use "raw" either, because we don't want to check inside C++11
|
||||||
|
# raw strings,
|
||||||
|
raw_lines = clean_lines.lines_without_raw_strings
|
||||||
line = raw_lines[linenum]
|
line = raw_lines[linenum]
|
||||||
|
|
||||||
if line.find('\t') != -1:
|
if line.find('\t') != -1:
|
||||||
@ -3287,7 +3605,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
|||||||
|
|
||||||
|
|
||||||
def _GetTextInside(text, start_pattern):
|
def _GetTextInside(text, start_pattern):
|
||||||
"""Retrieves all the text between matching open and close parentheses.
|
r"""Retrieves all the text between matching open and close parentheses.
|
||||||
|
|
||||||
Given a string of lines and a regular expression string, retrieve all the text
|
Given a string of lines and a regular expression string, retrieve all the text
|
||||||
following the expression and between opening punctuation symbols like
|
following the expression and between opening punctuation symbols like
|
||||||
@ -3343,10 +3661,20 @@ def _GetTextInside(text, start_pattern):
|
|||||||
|
|
||||||
|
|
||||||
# Patterns for matching call-by-reference parameters.
|
# Patterns for matching call-by-reference parameters.
|
||||||
|
#
|
||||||
|
# Supports nested templates up to 2 levels deep using this messy pattern:
|
||||||
|
# < (?: < (?: < [^<>]*
|
||||||
|
# >
|
||||||
|
# | [^<>] )*
|
||||||
|
# >
|
||||||
|
# | [^<>] )*
|
||||||
|
# >
|
||||||
_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
|
_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
|
||||||
_RE_PATTERN_TYPE = (
|
_RE_PATTERN_TYPE = (
|
||||||
r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
|
r'(?:const\s+)?(?:typename\s+|class\s+|struct\s+|union\s+|enum\s+)?'
|
||||||
r'[\w:]*\w(?:\s*<[\w:*, ]*>(?:::\w+)?)?')
|
r'(?:\w|'
|
||||||
|
r'\s*<(?:<(?:<[^<>]*>|[^<>])*>|[^<>])*>|'
|
||||||
|
r'::)+')
|
||||||
# A call-by-reference parameter ends with '& identifier'.
|
# A call-by-reference parameter ends with '& identifier'.
|
||||||
_RE_PATTERN_REF_PARAM = re.compile(
|
_RE_PATTERN_REF_PARAM = re.compile(
|
||||||
r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
|
r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
|
||||||
@ -3386,6 +3714,11 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
|||||||
CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
|
CheckIncludeLine(filename, clean_lines, linenum, include_state, error)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Reset include state across preprocessor directives. This is meant
|
||||||
|
# to silence warnings for conditional includes.
|
||||||
|
if Match(r'^\s*#\s*(?:ifdef|elif|else|endif)\b', line):
|
||||||
|
include_state.ResetSection()
|
||||||
|
|
||||||
# Make Windows paths like Unix.
|
# Make Windows paths like Unix.
|
||||||
fullname = os.path.abspath(filename).replace('\\', '/')
|
fullname = os.path.abspath(filename).replace('\\', '/')
|
||||||
|
|
||||||
@ -3485,8 +3818,13 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
|||||||
# Make sure it's not a function.
|
# Make sure it's not a function.
|
||||||
# Function template specialization looks like: "string foo<Type>(...".
|
# Function template specialization looks like: "string foo<Type>(...".
|
||||||
# Class template definitions look like: "string Foo<Type>::Method(...".
|
# Class template definitions look like: "string Foo<Type>::Method(...".
|
||||||
if match and not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)',
|
#
|
||||||
match.group(3)):
|
# Also ignore things that look like operators. These are matched separately
|
||||||
|
# because operator names cross non-word boundaries. If we change the pattern
|
||||||
|
# above, we would decrease the accuracy of matching identifiers.
|
||||||
|
if (match and
|
||||||
|
not Search(r'\boperator\W', line) and
|
||||||
|
not Match(r'\s*(<.*>)?(::[a-zA-Z0-9_]+)?\s*\(([^"]|$)', match.group(3))):
|
||||||
error(filename, linenum, 'runtime/string', 4,
|
error(filename, linenum, 'runtime/string', 4,
|
||||||
'For a static/global string constant, use a C style string instead: '
|
'For a static/global string constant, use a C style string instead: '
|
||||||
'"%schar %s[]".' %
|
'"%schar %s[]".' %
|
||||||
@ -3668,26 +4006,46 @@ def CheckForNonConstReference(filename, clean_lines, linenum,
|
|||||||
if '&' not in line:
|
if '&' not in line:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Long type names may be broken across multiple lines, with the
|
# Long type names may be broken across multiple lines, usually in one
|
||||||
# newline before or after the scope resolution operator. If we
|
# of these forms:
|
||||||
# detected a type split across two lines, join the previous line to
|
# LongType
|
||||||
# current line so that we can match const references accordingly.
|
# ::LongTypeContinued &identifier
|
||||||
|
# LongType::
|
||||||
|
# LongTypeContinued &identifier
|
||||||
|
# LongType<
|
||||||
|
# ...>::LongTypeContinued &identifier
|
||||||
|
#
|
||||||
|
# If we detected a type split across two lines, join the previous
|
||||||
|
# line to current line so that we can match const references
|
||||||
|
# accordingly.
|
||||||
#
|
#
|
||||||
# Note that this only scans back one line, since scanning back
|
# Note that this only scans back one line, since scanning back
|
||||||
# arbitrary number of lines would be expensive. If you have a type
|
# arbitrary number of lines would be expensive. If you have a type
|
||||||
# that spans more than 2 lines, please use a typedef.
|
# that spans more than 2 lines, please use a typedef.
|
||||||
if linenum > 1:
|
if linenum > 1:
|
||||||
previous = None
|
previous = None
|
||||||
if Match(r'\s*::(?:\w|::)+\s*&\s*\S', line):
|
if Match(r'\s*::(?:[\w<>]|::)+\s*&\s*\S', line):
|
||||||
# previous_line\n + ::current_line
|
# previous_line\n + ::current_line
|
||||||
previous = Search(r'\b((?:const\s*)?(?:\w|::)+\w)\s*$',
|
previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
|
||||||
clean_lines.elided[linenum - 1])
|
clean_lines.elided[linenum - 1])
|
||||||
elif Match(r'\s*[a-zA-Z_](\w|::)+\s*&\s*\S', line):
|
elif Match(r'\s*[a-zA-Z_]([\w<>]|::)+\s*&\s*\S', line):
|
||||||
# previous_line::\n + current_line
|
# previous_line::\n + current_line
|
||||||
previous = Search(r'\b((?:const\s*)?(?:\w|::)+::)\s*$',
|
previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
|
||||||
clean_lines.elided[linenum - 1])
|
clean_lines.elided[linenum - 1])
|
||||||
if previous:
|
if previous:
|
||||||
line = previous.group(1) + line.lstrip()
|
line = previous.group(1) + line.lstrip()
|
||||||
|
else:
|
||||||
|
# Check for templated parameter that is split across multiple lines
|
||||||
|
endpos = line.rfind('>')
|
||||||
|
if endpos > -1:
|
||||||
|
(_, startline, startpos) = ReverseCloseExpression(
|
||||||
|
clean_lines, linenum, endpos)
|
||||||
|
if startpos > -1 and startline < linenum:
|
||||||
|
# Found the matching < on an earlier line, collect all
|
||||||
|
# pieces up to current line.
|
||||||
|
line = ''
|
||||||
|
for i in xrange(startline, linenum + 1):
|
||||||
|
line += clean_lines.elided[i].strip()
|
||||||
|
|
||||||
# Check for non-const references in function parameters. A single '&' may
|
# Check for non-const references in function parameters. A single '&' may
|
||||||
# found in the following places:
|
# found in the following places:
|
||||||
@ -3737,7 +4095,8 @@ def CheckForNonConstReference(filename, clean_lines, linenum,
|
|||||||
if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
|
if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
|
||||||
error(filename, linenum, 'runtime/references', 2,
|
error(filename, linenum, 'runtime/references', 2,
|
||||||
'Is this a non-const reference? '
|
'Is this a non-const reference? '
|
||||||
'If so, make const or use a pointer: ' + parameter)
|
'If so, make const or use a pointer: ' +
|
||||||
|
ReplaceAll(' *<', '<', parameter))
|
||||||
|
|
||||||
|
|
||||||
def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
|
def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
|
||||||
@ -4084,8 +4443,7 @@ def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
|
|||||||
linenum: The number of the line to check.
|
linenum: The number of the line to check.
|
||||||
error: The function to call with any errors found.
|
error: The function to call with any errors found.
|
||||||
"""
|
"""
|
||||||
raw = clean_lines.raw_lines
|
line = clean_lines.elided[linenum]
|
||||||
line = raw[linenum]
|
|
||||||
match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
|
match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
|
||||||
if match:
|
if match:
|
||||||
error(filename, linenum, 'build/explicit_make_pair',
|
error(filename, linenum, 'build/explicit_make_pair',
|
||||||
@ -4128,6 +4486,7 @@ def ProcessLine(filename, file_extension, clean_lines, line,
|
|||||||
CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
|
CheckForNonConstReference(filename, clean_lines, line, nesting_state, error)
|
||||||
CheckForNonStandardConstructs(filename, clean_lines, line,
|
CheckForNonStandardConstructs(filename, clean_lines, line,
|
||||||
nesting_state, error)
|
nesting_state, error)
|
||||||
|
CheckVlogArguments(filename, clean_lines, line, error)
|
||||||
CheckPosixThreading(filename, clean_lines, line, error)
|
CheckPosixThreading(filename, clean_lines, line, error)
|
||||||
CheckInvalidIncrement(filename, clean_lines, line, error)
|
CheckInvalidIncrement(filename, clean_lines, line, error)
|
||||||
CheckMakePairUsesDeduction(filename, clean_lines, line, error)
|
CheckMakePairUsesDeduction(filename, clean_lines, line, error)
|
||||||
@ -4169,13 +4528,13 @@ def ProcessFileData(filename, file_extension, lines, error,
|
|||||||
ProcessLine(filename, file_extension, clean_lines, line,
|
ProcessLine(filename, file_extension, clean_lines, line,
|
||||||
include_state, function_state, nesting_state, error,
|
include_state, function_state, nesting_state, error,
|
||||||
extra_check_functions)
|
extra_check_functions)
|
||||||
nesting_state.CheckClassFinished(filename, error)
|
nesting_state.CheckCompletedBlocks(filename, error)
|
||||||
|
|
||||||
CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
|
CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
|
||||||
|
|
||||||
# We check here rather than inside ProcessLine so that we see raw
|
# We check here rather than inside ProcessLine so that we see raw
|
||||||
# lines rather than "cleaned" lines.
|
# lines rather than "cleaned" lines.
|
||||||
CheckForUnicodeReplacementCharacters(filename, lines, error)
|
CheckForBadCharacters(filename, lines, error)
|
||||||
|
|
||||||
CheckForNewlineAtEOF(filename, lines, error)
|
CheckForNewlineAtEOF(filename, lines, error)
|
||||||
|
|
||||||
@ -4231,9 +4590,10 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
|
|||||||
|
|
||||||
# When reading from stdin, the extension is unknown, so no cpplint tests
|
# When reading from stdin, the extension is unknown, so no cpplint tests
|
||||||
# should rely on the extension.
|
# should rely on the extension.
|
||||||
if (filename != '-' and file_extension != 'cc' and file_extension != 'h'
|
valid_extensions = ['cc', 'h', 'cpp', 'cu', 'cuh']
|
||||||
and file_extension != 'cpp'):
|
if filename != '-' and file_extension not in valid_extensions:
|
||||||
sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename)
|
sys.stderr.write('Ignoring %s; not a valid file name '
|
||||||
|
'(.cc, .h, .cpp, .cu, .cuh)\n' % filename)
|
||||||
else:
|
else:
|
||||||
ProcessFileData(filename, file_extension, lines, Error,
|
ProcessFileData(filename, file_extension, lines, Error,
|
||||||
extra_check_functions)
|
extra_check_functions)
|
||||||
|
@ -139,7 +139,7 @@ class CpplintTestBase(unittest.TestCase):
|
|||||||
error_collector)
|
error_collector)
|
||||||
cpplint.CheckForNonStandardConstructs('foo.h', lines, i,
|
cpplint.CheckForNonStandardConstructs('foo.h', lines, i,
|
||||||
nesting_state, error_collector)
|
nesting_state, error_collector)
|
||||||
nesting_state.CheckClassFinished('foo.h', error_collector)
|
nesting_state.CheckCompletedBlocks('foo.h', error_collector)
|
||||||
return error_collector.Results()
|
return error_collector.Results()
|
||||||
|
|
||||||
# Similar to PerformMultiLineLint, but calls CheckLanguage instead of
|
# Similar to PerformMultiLineLint, but calls CheckLanguage instead of
|
||||||
@ -230,13 +230,13 @@ class CpplintTestBase(unittest.TestCase):
|
|||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
start_errors,
|
start_errors,
|
||||||
error_collector.Results().count(
|
error_collector.Results().count(
|
||||||
'Blank line at the start of a code block. Is this needed?'
|
'Redundant blank line at the start of a code block '
|
||||||
' [whitespace/blank_line] [2]'))
|
'should be deleted. [whitespace/blank_line] [2]'))
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
end_errors,
|
end_errors,
|
||||||
error_collector.Results().count(
|
error_collector.Results().count(
|
||||||
'Blank line at the end of a code block. Is this needed?'
|
'Redundant blank line at the end of a code block '
|
||||||
' [whitespace/blank_line] [3]'))
|
'should be deleted. [whitespace/blank_line] [3]'))
|
||||||
|
|
||||||
|
|
||||||
class CpplintTest(CpplintTestBase):
|
class CpplintTest(CpplintTestBase):
|
||||||
@ -832,10 +832,55 @@ class CpplintTest(CpplintTestBase):
|
|||||||
self.assertEqual('f(a, b);',
|
self.assertEqual('f(a, b);',
|
||||||
cpplint.CleanseComments('f(a, /* name */ b);'))
|
cpplint.CleanseComments('f(a, /* name */ b);'))
|
||||||
self.assertEqual('f(a, b);',
|
self.assertEqual('f(a, b);',
|
||||||
cpplint.CleanseComments('f(a /* name */, b);'))
|
cpplint.CleanseComments('f(a /* name */, b);'))
|
||||||
self.assertEqual('f(a, b);',
|
self.assertEqual('f(a, b);',
|
||||||
cpplint.CleanseComments('f(a, /* name */b);'))
|
cpplint.CleanseComments('f(a, /* name */b);'))
|
||||||
|
|
||||||
|
def testRawStrings(self):
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
"""
|
||||||
|
void Func() {
|
||||||
|
static const char kString[] = R"(
|
||||||
|
#endif <- invalid preprocessor should be ignored
|
||||||
|
*/ <- invalid comment should be ignored too
|
||||||
|
)";
|
||||||
|
}""",
|
||||||
|
'')
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
"""
|
||||||
|
void Func() {
|
||||||
|
string s = R"TrueDelimiter(
|
||||||
|
)"
|
||||||
|
)FalseDelimiter"
|
||||||
|
)TrueDelimiter";
|
||||||
|
}""",
|
||||||
|
'')
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
"""
|
||||||
|
void Func() {
|
||||||
|
char char kString[] = R"( ";" )";
|
||||||
|
}""",
|
||||||
|
'')
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
"""
|
||||||
|
static const char kRawString[] = R"(
|
||||||
|
\tstatic const int kLineWithTab = 1;
|
||||||
|
static const int kLineWithTrailingWhiteSpace = 1;\x20
|
||||||
|
|
||||||
|
void WeirdNumberOfSpacesAtLineStart() {
|
||||||
|
string x;
|
||||||
|
x += StrCat("Use StrAppend instead");
|
||||||
|
}
|
||||||
|
|
||||||
|
void BlankLineAtEndOfBlock() {
|
||||||
|
// TODO incorrectly formatted
|
||||||
|
//Badly formatted comment
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
)";""",
|
||||||
|
'')
|
||||||
|
|
||||||
def testMultiLineComments(self):
|
def testMultiLineComments(self):
|
||||||
# missing explicit is bad
|
# missing explicit is bad
|
||||||
self.TestMultiLineLint(
|
self.TestMultiLineLint(
|
||||||
@ -859,8 +904,8 @@ class CpplintTest(CpplintTestBase):
|
|||||||
def testMultilineStrings(self):
|
def testMultilineStrings(self):
|
||||||
multiline_string_error_message = (
|
multiline_string_error_message = (
|
||||||
'Multi-line string ("...") found. This lint script doesn\'t '
|
'Multi-line string ("...") found. This lint script doesn\'t '
|
||||||
'do well with such strings, and may give bogus warnings. They\'re '
|
'do well with such strings, and may give bogus warnings. '
|
||||||
'ugly and unnecessary, and you should use concatenation instead".'
|
'Use C++11 raw strings or concatenation instead.'
|
||||||
' [readability/multiline_string] [5]')
|
' [readability/multiline_string] [5]')
|
||||||
|
|
||||||
file_path = 'mydir/foo.cc'
|
file_path = 'mydir/foo.cc'
|
||||||
@ -1151,6 +1196,31 @@ class CpplintTest(CpplintTestBase):
|
|||||||
' for improved thread safety.'
|
' for improved thread safety.'
|
||||||
' [runtime/threadsafe_fn] [2]')
|
' [runtime/threadsafe_fn] [2]')
|
||||||
|
|
||||||
|
def testVlogMisuse(self):
|
||||||
|
self.TestLint('VLOG(1)', '')
|
||||||
|
self.TestLint('VLOG(99)', '')
|
||||||
|
self.TestLint('LOG(ERROR)', '')
|
||||||
|
self.TestLint('LOG(INFO)', '')
|
||||||
|
self.TestLint('LOG(WARNING)', '')
|
||||||
|
self.TestLint('LOG(FATAL)', '')
|
||||||
|
self.TestLint('LOG(DFATAL)', '')
|
||||||
|
self.TestLint('VLOG(SOMETHINGWEIRD)', '')
|
||||||
|
self.TestLint('MYOWNVLOG(ERROR)', '')
|
||||||
|
errmsg = ('VLOG() should be used with numeric verbosity level. '
|
||||||
|
'Use LOG() if you want symbolic severity levels.'
|
||||||
|
' [runtime/vlog] [5]')
|
||||||
|
self.TestLint('VLOG(ERROR)', errmsg)
|
||||||
|
self.TestLint('VLOG(INFO)', errmsg)
|
||||||
|
self.TestLint('VLOG(WARNING)', errmsg)
|
||||||
|
self.TestLint('VLOG(FATAL)', errmsg)
|
||||||
|
self.TestLint('VLOG(DFATAL)', errmsg)
|
||||||
|
self.TestLint(' VLOG(ERROR)', errmsg)
|
||||||
|
self.TestLint(' VLOG(INFO)', errmsg)
|
||||||
|
self.TestLint(' VLOG(WARNING)', errmsg)
|
||||||
|
self.TestLint(' VLOG(FATAL)', errmsg)
|
||||||
|
self.TestLint(' VLOG(DFATAL)', errmsg)
|
||||||
|
|
||||||
|
|
||||||
# Test potential format string bugs like printf(foo).
|
# Test potential format string bugs like printf(foo).
|
||||||
def testFormatStrings(self):
|
def testFormatStrings(self):
|
||||||
self.TestLint('printf("foo")', '')
|
self.TestLint('printf("foo")', '')
|
||||||
@ -1632,6 +1702,11 @@ class CpplintTest(CpplintTestBase):
|
|||||||
self.TestLint('T& r = v', '')
|
self.TestLint('T& r = v', '')
|
||||||
self.TestLint('static_assert((kBits & kMask) == 0, "text");', '')
|
self.TestLint('static_assert((kBits & kMask) == 0, "text");', '')
|
||||||
self.TestLint('COMPILE_ASSERT((kBits & kMask) == 0, text);', '')
|
self.TestLint('COMPILE_ASSERT((kBits & kMask) == 0, text);', '')
|
||||||
|
# Spaces before template arguments. This is poor style, but
|
||||||
|
# happens 0.15% of the time.
|
||||||
|
self.TestLint('void Func(const vector <int> &const_x, '
|
||||||
|
'vector <int> &nonconst_x) {',
|
||||||
|
operand_error_message % 'vector<int> &nonconst_x')
|
||||||
|
|
||||||
# Another potential false positive. This one needs full parser
|
# Another potential false positive. This one needs full parser
|
||||||
# state to reproduce as opposed to just TestLint.
|
# state to reproduce as opposed to just TestLint.
|
||||||
@ -1662,16 +1737,21 @@ class CpplintTest(CpplintTestBase):
|
|||||||
' Inner& const_x,',
|
' Inner& const_x,',
|
||||||
' const Outer',
|
' const Outer',
|
||||||
' ::Inner& const_y,',
|
' ::Inner& const_y,',
|
||||||
|
' const Outer<',
|
||||||
|
' int>::Inner& const_z,',
|
||||||
' Outer::',
|
' Outer::',
|
||||||
' Inner& nonconst_x,',
|
' Inner& nonconst_x,',
|
||||||
' Outer',
|
' Outer',
|
||||||
' ::Inner& nonconst_y) {',
|
' ::Inner& nonconst_y,',
|
||||||
|
' Outer<',
|
||||||
|
' int>::Inner& nonconst_z) {',
|
||||||
'}',
|
'}',
|
||||||
''],
|
''],
|
||||||
error_collector)
|
error_collector)
|
||||||
self.assertEquals(
|
self.assertEquals(
|
||||||
[operand_error_message % 'Outer::Inner& nonconst_x',
|
[operand_error_message % 'Outer::Inner& nonconst_x',
|
||||||
operand_error_message % 'Outer::Inner& nonconst_y'],
|
operand_error_message % 'Outer::Inner& nonconst_y',
|
||||||
|
operand_error_message % 'Outer<int>::Inner& nonconst_z'],
|
||||||
error_collector.Results())
|
error_collector.Results())
|
||||||
|
|
||||||
def testBraceAtBeginOfLine(self):
|
def testBraceAtBeginOfLine(self):
|
||||||
@ -1705,6 +1785,14 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'{ should almost always be at the end of the previous line'
|
'{ should almost always be at the end of the previous line'
|
||||||
' [whitespace/braces] [4]'))
|
' [whitespace/braces] [4]'))
|
||||||
|
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
"""
|
||||||
|
foo(
|
||||||
|
{
|
||||||
|
loooooooooooooooong_value,
|
||||||
|
});""",
|
||||||
|
'')
|
||||||
|
|
||||||
def testMismatchingSpacesInParens(self):
|
def testMismatchingSpacesInParens(self):
|
||||||
self.TestLint('if (foo ) {', 'Mismatching spaces inside () in if'
|
self.TestLint('if (foo ) {', 'Mismatching spaces inside () in if'
|
||||||
' [whitespace/parens] [5]')
|
' [whitespace/parens] [5]')
|
||||||
@ -1758,6 +1846,7 @@ class CpplintTest(CpplintTestBase):
|
|||||||
self.TestLint('typedef foo (*foo12bar_)(foo)', '')
|
self.TestLint('typedef foo (*foo12bar_)(foo)', '')
|
||||||
self.TestLint('typedef foo (Foo::*bar)(foo)', '')
|
self.TestLint('typedef foo (Foo::*bar)(foo)', '')
|
||||||
self.TestLint('foo (Foo::*bar)(', '')
|
self.TestLint('foo (Foo::*bar)(', '')
|
||||||
|
self.TestLint('foo (x::y::*z)(', '')
|
||||||
self.TestLint('foo (Foo::bar)(',
|
self.TestLint('foo (Foo::bar)(',
|
||||||
'Extra space before ( in function call'
|
'Extra space before ( in function call'
|
||||||
' [whitespace/parens] [4]')
|
' [whitespace/parens] [4]')
|
||||||
@ -1769,6 +1858,13 @@ class CpplintTest(CpplintTestBase):
|
|||||||
self.TestLint('char (*p)[sizeof(foo)] = &foo', '')
|
self.TestLint('char (*p)[sizeof(foo)] = &foo', '')
|
||||||
self.TestLint('char (&ref)[sizeof(foo)] = &foo', '')
|
self.TestLint('char (&ref)[sizeof(foo)] = &foo', '')
|
||||||
self.TestLint('const char32 (*table[])[6];', '')
|
self.TestLint('const char32 (*table[])[6];', '')
|
||||||
|
# The sizeof operator is often written as if it were a function call, with
|
||||||
|
# an opening parenthesis directly following the operator name, but it can
|
||||||
|
# also be written like any other operator, with a space following the
|
||||||
|
# operator name, and the argument optionally in parentheses.
|
||||||
|
self.TestLint('sizeof(foo)', '')
|
||||||
|
self.TestLint('sizeof foo', '')
|
||||||
|
self.TestLint('sizeof (foo)', '')
|
||||||
|
|
||||||
def testSpacingBeforeBraces(self):
|
def testSpacingBeforeBraces(self):
|
||||||
self.TestLint('if (foo){', 'Missing space before {'
|
self.TestLint('if (foo){', 'Missing space before {'
|
||||||
@ -1781,11 +1877,63 @@ class CpplintTest(CpplintTestBase):
|
|||||||
def testSemiColonAfterBraces(self):
|
def testSemiColonAfterBraces(self):
|
||||||
self.TestLint('if (cond) {};',
|
self.TestLint('if (cond) {};',
|
||||||
'You don\'t need a ; after a } [readability/braces] [4]')
|
'You don\'t need a ; after a } [readability/braces] [4]')
|
||||||
|
self.TestLint('void Func() {};',
|
||||||
|
'You don\'t need a ; after a } [readability/braces] [4]')
|
||||||
|
self.TestLint('void Func() const {};',
|
||||||
|
'You don\'t need a ; after a } [readability/braces] [4]')
|
||||||
self.TestLint('class X {};', '')
|
self.TestLint('class X {};', '')
|
||||||
self.TestLint('struct X {};', '')
|
self.TestLint('struct X {};', '')
|
||||||
self.TestLint('union {} x;', '')
|
self.TestLint('union {} x;', '')
|
||||||
self.TestLint('union {};', '')
|
self.TestLint('union {};', '')
|
||||||
self.TestLint('union {};', '')
|
|
||||||
|
self.TestLint('class X : public Y {};', '')
|
||||||
|
self.TestLint('class X : public MACRO() {};', '')
|
||||||
|
self.TestLint('DEFINE_FACADE(PCQueue::Watcher, PCQueue) {};', '')
|
||||||
|
self.TestLint('VCLASS(XfaTest, XfaContextTest) {};', '')
|
||||||
|
self.TestLint('TEST(TestCase, TestName) {};',
|
||||||
|
'You don\'t need a ; after a } [readability/braces] [4]')
|
||||||
|
self.TestLint('TEST_F(TestCase, TestName) {};',
|
||||||
|
'You don\'t need a ; after a } [readability/braces] [4]')
|
||||||
|
|
||||||
|
self.TestLint('file_tocs_[i] = (FileToc) {a, b, c};', '')
|
||||||
|
self.TestMultiLineLint('class X : public Y,\npublic Z {};', '')
|
||||||
|
|
||||||
|
def testBraceInitializerList(self):
|
||||||
|
self.TestLint('MyStruct p = {1, 2};', '')
|
||||||
|
self.TestLint('MyStruct p{1, 2};', '')
|
||||||
|
self.TestLint('vector<int> p = {1, 2};', '')
|
||||||
|
self.TestLint('vector<int> p{1, 2};', '')
|
||||||
|
self.TestLint('x = vector<int>{1, 2};', '')
|
||||||
|
self.TestLint('x = (struct in_addr){ 0 };', '')
|
||||||
|
self.TestLint('Func(vector<int>{1, 2})', '')
|
||||||
|
self.TestLint('Func((struct in_addr){ 0 })', '')
|
||||||
|
self.TestLint('Func(vector<int>{1, 2}, 3)', '')
|
||||||
|
self.TestLint('Func((struct in_addr){ 0 }, 3)', '')
|
||||||
|
self.TestLint('LOG(INFO) << char{7};', '')
|
||||||
|
self.TestLint('LOG(INFO) << char{7} << "!";', '')
|
||||||
|
self.TestLint('int p[2] = {1, 2};', '')
|
||||||
|
self.TestLint('return {1, 2};', '')
|
||||||
|
self.TestLint('std::unique_ptr<Foo> foo{new Foo{}};', '')
|
||||||
|
self.TestLint('auto foo = std::unique_ptr<Foo>{new Foo{}};', '')
|
||||||
|
self.TestLint('static_assert(Max7String{}.IsValid(), "");', '')
|
||||||
|
self.TestLint('map_of_pairs[{1, 2}] = 3;', '')
|
||||||
|
|
||||||
|
self.TestMultiLineLint('std::unique_ptr<Foo> foo{\n'
|
||||||
|
' new Foo{}\n'
|
||||||
|
'};\n', '')
|
||||||
|
self.TestMultiLineLint('std::unique_ptr<Foo> foo{\n'
|
||||||
|
' new Foo{\n'
|
||||||
|
' new Bar{}\n'
|
||||||
|
' }\n'
|
||||||
|
'};\n', '')
|
||||||
|
self.TestMultiLineLint('if (true) {\n'
|
||||||
|
' if (false){}\n'
|
||||||
|
'}\n',
|
||||||
|
'Missing space before { [whitespace/braces] [5]')
|
||||||
|
self.TestMultiLineLint('MyClass::MyClass()\n'
|
||||||
|
' : initializer_{\n'
|
||||||
|
' Func()} {\n'
|
||||||
|
'}\n', '')
|
||||||
|
|
||||||
def testSpacingAroundElse(self):
|
def testSpacingAroundElse(self):
|
||||||
self.TestLint('}else {', 'Missing space before else'
|
self.TestLint('}else {', 'Missing space before else'
|
||||||
@ -1940,6 +2088,8 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'For a static/global string constant, use a C style '
|
'For a static/global string constant, use a C style '
|
||||||
'string instead: "char Foo::bar[]".'
|
'string instead: "char Foo::bar[]".'
|
||||||
' [runtime/string] [4]')
|
' [runtime/string] [4]')
|
||||||
|
self.TestLint('string Foo::bar() {}', '')
|
||||||
|
self.TestLint('string Foo::operator*() {}', '')
|
||||||
# Rare case.
|
# Rare case.
|
||||||
self.TestLint('string foo("foobar");',
|
self.TestLint('string foo("foobar");',
|
||||||
'For a static/global string constant, use a C style '
|
'For a static/global string constant, use a C style '
|
||||||
@ -2113,6 +2263,30 @@ class CpplintTest(CpplintTestBase):
|
|||||||
# you can see by evaluating codecs.getencoder('utf8')(u'\ufffd')).
|
# you can see by evaluating codecs.getencoder('utf8')(u'\ufffd')).
|
||||||
DoTest(self, '\xef\xbf\xbd\n', True)
|
DoTest(self, '\xef\xbf\xbd\n', True)
|
||||||
|
|
||||||
|
def testBadCharacters(self):
|
||||||
|
# Test for NUL bytes only
|
||||||
|
error_collector = ErrorCollector(self.assert_)
|
||||||
|
cpplint.ProcessFileData('nul.cc', 'cc',
|
||||||
|
['// Copyright 2008 Your Company.',
|
||||||
|
'\0', ''], error_collector)
|
||||||
|
self.assertEquals(
|
||||||
|
error_collector.Results(),
|
||||||
|
'Line contains NUL byte. [readability/nul] [5]')
|
||||||
|
|
||||||
|
# Make sure both NUL bytes and UTF-8 are caught if they appear on
|
||||||
|
# the same line.
|
||||||
|
error_collector = ErrorCollector(self.assert_)
|
||||||
|
cpplint.ProcessFileData(
|
||||||
|
'nul_utf8.cc', 'cc',
|
||||||
|
['// Copyright 2008 Your Company.',
|
||||||
|
unicode('\xe9x\0', 'utf8', 'replace'), ''],
|
||||||
|
error_collector)
|
||||||
|
self.assertEquals(
|
||||||
|
error_collector.Results(),
|
||||||
|
['Line contains invalid UTF-8 (or Unicode replacement character).'
|
||||||
|
' [readability/utf8] [5]',
|
||||||
|
'Line contains NUL byte. [readability/nul] [5]'])
|
||||||
|
|
||||||
def testIsBlankLine(self):
|
def testIsBlankLine(self):
|
||||||
self.assert_(cpplint.IsBlankLine(''))
|
self.assert_(cpplint.IsBlankLine(''))
|
||||||
self.assert_(cpplint.IsBlankLine(' '))
|
self.assert_(cpplint.IsBlankLine(' '))
|
||||||
@ -2150,7 +2324,7 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'}'],
|
'}'],
|
||||||
error_collector)
|
error_collector)
|
||||||
self.assertEquals(0, error_collector.Results().count(
|
self.assertEquals(0, error_collector.Results().count(
|
||||||
'Blank line at the end of a code block. Is this needed?'
|
'Redundant blank line at the end of a code block should be deleted.'
|
||||||
' [whitespace/blank_line] [3]'))
|
' [whitespace/blank_line] [3]'))
|
||||||
|
|
||||||
def testAllowBlankLineBeforeIfElseChain(self):
|
def testAllowBlankLineBeforeIfElseChain(self):
|
||||||
@ -2167,7 +2341,7 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'}'],
|
'}'],
|
||||||
error_collector)
|
error_collector)
|
||||||
self.assertEquals(1, error_collector.Results().count(
|
self.assertEquals(1, error_collector.Results().count(
|
||||||
'Blank line at the end of a code block. Is this needed?'
|
'Redundant blank line at the end of a code block should be deleted.'
|
||||||
' [whitespace/blank_line] [3]'))
|
' [whitespace/blank_line] [3]'))
|
||||||
|
|
||||||
def testBlankLineBeforeSectionKeyword(self):
|
def testBlankLineBeforeSectionKeyword(self):
|
||||||
@ -2335,6 +2509,8 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'Missing space after , [whitespace/comma] [3]'])
|
'Missing space after , [whitespace/comma] [3]'])
|
||||||
self.TestLint('f(a, /* name */ b);', '')
|
self.TestLint('f(a, /* name */ b);', '')
|
||||||
self.TestLint('f(a, /* name */b);', '')
|
self.TestLint('f(a, /* name */b);', '')
|
||||||
|
self.TestLint('f(a, /* name */-1);', '')
|
||||||
|
self.TestLint('f(a, /* name */"1");', '')
|
||||||
self.TestLint('f(1, /* empty macro arg */, 2)', '')
|
self.TestLint('f(1, /* empty macro arg */, 2)', '')
|
||||||
self.TestLint('f(1,, 2)', '')
|
self.TestLint('f(1,, 2)', '')
|
||||||
|
|
||||||
@ -2542,6 +2718,11 @@ class CpplintTest(CpplintTestBase):
|
|||||||
'class Foo {',
|
'class Foo {',
|
||||||
'Failed to find complete declaration of class Foo'
|
'Failed to find complete declaration of class Foo'
|
||||||
' [build/class] [5]')
|
' [build/class] [5]')
|
||||||
|
# Do the same for namespaces
|
||||||
|
self.TestMultiLineLint(
|
||||||
|
'namespace Foo {',
|
||||||
|
'Failed to find complete declaration of namespace Foo'
|
||||||
|
' [build/namespaces] [5]')
|
||||||
# Don't warn on forward declarations of various types.
|
# Don't warn on forward declarations of various types.
|
||||||
self.TestMultiLineLint(
|
self.TestMultiLineLint(
|
||||||
'class Foo;',
|
'class Foo;',
|
||||||
@ -3256,6 +3437,34 @@ class OrderOfIncludesTest(CpplintTestBase):
|
|||||||
'Include "a/b.h" not in alphabetical '
|
'Include "a/b.h" not in alphabetical '
|
||||||
'order [build/include_alpha] [4]')
|
'order [build/include_alpha] [4]')
|
||||||
|
|
||||||
|
# Test conditional includes
|
||||||
|
self.TestLanguageRulesCheck(
|
||||||
|
'a/a.cc',
|
||||||
|
''.join(['#include <string.h>\n',
|
||||||
|
'#include "base/port.h"\n',
|
||||||
|
'#include <initializer_list>\n']),
|
||||||
|
('Found C++ system header after other header. '
|
||||||
|
'Should be: a.h, c system, c++ system, other. '
|
||||||
|
'[build/include_order] [4]'))
|
||||||
|
self.TestLanguageRulesCheck(
|
||||||
|
'a/a.cc',
|
||||||
|
''.join(['#include <string.h>\n',
|
||||||
|
'#include "base/port.h"\n',
|
||||||
|
'#ifdef LANG_CXX11\n',
|
||||||
|
'#include <initializer_list>\n',
|
||||||
|
'#endif // LANG_CXX11\n']),
|
||||||
|
'')
|
||||||
|
self.TestLanguageRulesCheck(
|
||||||
|
'a/a.cc',
|
||||||
|
''.join(['#include <string.h>\n',
|
||||||
|
'#ifdef LANG_CXX11\n',
|
||||||
|
'#include "base/port.h"\n',
|
||||||
|
'#include <initializer_list>\n',
|
||||||
|
'#endif // LANG_CXX11\n']),
|
||||||
|
('Found C++ system header after other header. '
|
||||||
|
'Should be: a.h, c system, c++ system, other. '
|
||||||
|
'[build/include_order] [4]'))
|
||||||
|
|
||||||
|
|
||||||
class CheckForFunctionLengthsTest(CpplintTestBase):
|
class CheckForFunctionLengthsTest(CpplintTestBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user