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=.
|
||||
# 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.
|
||||
# \ used for clearer layout -- pylint: disable-msg=C6013
|
||||
_ERROR_CATEGORIES = [
|
||||
'build/class',
|
||||
'build/deprecated',
|
||||
|
@ -151,6 +150,7 @@ _ERROR_CATEGORIES = [
|
|||
'readability/multiline_string',
|
||||
'readability/namespace',
|
||||
'readability/nolint',
|
||||
'readability/nul',
|
||||
'readability/streams',
|
||||
'readability/todo',
|
||||
'readability/utf8',
|
||||
|
@ -168,15 +168,16 @@ _ERROR_CATEGORIES = [
|
|||
'runtime/references',
|
||||
'runtime/string',
|
||||
'runtime/threadsafe_fn',
|
||||
'whitespace/blank_line',
|
||||
'whitespace/braces',
|
||||
'whitespace/comma',
|
||||
'whitespace/comments',
|
||||
'whitespace/empty_conditional_body',
|
||||
'whitespace/empty_loop_body',
|
||||
'whitespace/end_of_line',
|
||||
'whitespace/ending_newline',
|
||||
'whitespace/forcolon',
|
||||
'runtime/vlog',
|
||||
'whitespace/blank_line',
|
||||
'whitespace/braces',
|
||||
'whitespace/comma',
|
||||
'whitespace/comments',
|
||||
'whitespace/empty_conditional_body',
|
||||
'whitespace/empty_loop_body',
|
||||
'whitespace/end_of_line',
|
||||
'whitespace/ending_newline',
|
||||
'whitespace/forcolon',
|
||||
'whitespace/indent',
|
||||
'whitespace/line_length',
|
||||
'whitespace/newline',
|
||||
|
@ -547,6 +548,9 @@ class _IncludeState(dict):
|
|||
|
||||
def __init__(self):
|
||||
dict.__init__(self)
|
||||
self.ResetSection()
|
||||
|
||||
def ResetSection(self):
|
||||
# The name of the current section.
|
||||
self._section = self._INITIAL_SECTION
|
||||
# The path of last found header.
|
||||
|
@ -981,7 +985,7 @@ def Error(filename, linenum, category, confidence, message):
|
|||
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(
|
||||
r'\\([abfnrtv?"\\\']|\d+|x[0-9a-fA-F]+)')
|
||||
# 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
|
||||
|
||||
|
||||
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):
|
||||
"""Find the beginning marker for a multiline comment."""
|
||||
while lineix < len(lines):
|
||||
|
@ -1094,9 +1159,11 @@ class CleansedLines(object):
|
|||
self.lines = []
|
||||
self.raw_lines = lines
|
||||
self.num_lines = len(lines)
|
||||
for linenum in range(len(lines)):
|
||||
self.lines.append(CleanseComments(lines[linenum]))
|
||||
elided = self._CollapseStrings(lines[linenum])
|
||||
self.lines_without_raw_strings = CleanseRawStrings(lines)
|
||||
for linenum in range(len(self.lines_without_raw_strings)):
|
||||
self.lines.append(CleanseComments(
|
||||
self.lines_without_raw_strings[linenum]))
|
||||
elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
|
||||
self.elided.append(CleanseComments(elided))
|
||||
|
||||
def NumLines(self):
|
||||
|
@ -1136,7 +1203,8 @@ def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
|
|||
endchar: expression closing character.
|
||||
|
||||
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)):
|
||||
if line[i] == startchar:
|
||||
|
@ -1144,14 +1212,14 @@ def FindEndOfExpressionInLine(line, startpos, depth, startchar, endchar):
|
|||
elif line[i] == endchar:
|
||||
depth -= 1
|
||||
if depth == 0:
|
||||
return i + 1
|
||||
return -1
|
||||
return (i + 1, 0)
|
||||
return (-1, depth)
|
||||
|
||||
|
||||
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.
|
||||
|
||||
Args:
|
||||
|
@ -1168,30 +1236,104 @@ def CloseExpression(clean_lines, linenum, pos):
|
|||
|
||||
line = clean_lines.elided[linenum]
|
||||
startchar = line[pos]
|
||||
if startchar not in '({[':
|
||||
if startchar not in '({[<':
|
||||
return (line, clean_lines.NumLines(), -1)
|
||||
if startchar == '(': endchar = ')'
|
||||
if startchar == '[': endchar = ']'
|
||||
if startchar == '{': endchar = '}'
|
||||
if startchar == '<': endchar = '>'
|
||||
|
||||
# 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:
|
||||
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:
|
||||
linenum += 1
|
||||
line = clean_lines.elided[linenum]
|
||||
delta = line.count(startchar) - line.count(endchar)
|
||||
if num_open + delta <= 0:
|
||||
return (line, linenum,
|
||||
FindEndOfExpressionInLine(line, 0, num_open, startchar, endchar))
|
||||
num_open += delta
|
||||
(end_pos, num_open) = FindEndOfExpressionInLine(
|
||||
line, 0, num_open, startchar, endchar)
|
||||
if end_pos > -1:
|
||||
return (line, linenum, end_pos)
|
||||
|
||||
# Did not find endchar before end of file, give up
|
||||
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):
|
||||
"""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)
|
||||
|
||||
|
||||
def CheckForUnicodeReplacementCharacters(filename, lines, error):
|
||||
"""Logs an error for each line containing Unicode replacement characters.
|
||||
def CheckForBadCharacters(filename, lines, error):
|
||||
"""Logs an error for each line containing bad characters.
|
||||
|
||||
These indicate that either the file 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.
|
||||
Two kinds of bad characters:
|
||||
|
||||
1. Unicode replacement characters: These indicate that either the file
|
||||
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:
|
||||
filename: The name of the current file.
|
||||
|
@ -1321,6 +1467,8 @@ def CheckForUnicodeReplacementCharacters(filename, lines, error):
|
|||
if u'\ufffd' in line:
|
||||
error(filename, linenum, 'readability/utf8', 5,
|
||||
'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):
|
||||
|
@ -1375,8 +1523,8 @@ def CheckForMultilineCommentsAndStrings(filename, clean_lines, linenum, error):
|
|||
if (line.count('"') - line.count('\\"')) % 2:
|
||||
error(filename, linenum, 'readability/multiline_string', 5,
|
||||
'Multi-line string ("...") found. This lint script doesn\'t '
|
||||
'do well with such strings, and may give bogus warnings. They\'re '
|
||||
'ugly and unnecessary, and you should use concatenation instead".')
|
||||
'do well with such strings, and may give bogus warnings. '
|
||||
'Use C++11 raw strings or concatenation instead.')
|
||||
|
||||
|
||||
threading_list = (
|
||||
|
@ -1413,7 +1561,7 @@ def CheckPosixThreading(filename, clean_lines, linenum, error):
|
|||
line = clean_lines.elided[linenum]
|
||||
for single_thread_function, multithread_safe_function in threading_list:
|
||||
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
|
||||
line[ix - 1] not in ('_', '.', '>'))):
|
||||
error(filename, linenum, 'runtime/threadsafe_fn', 2,
|
||||
|
@ -1422,6 +1570,25 @@ def CheckPosixThreading(filename, clean_lines, linenum, error):
|
|||
'...) 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
|
||||
# incrementing a value.
|
||||
_RE_PATTERN_INVALID_INCREMENT = re.compile(
|
||||
|
@ -1867,8 +2034,8 @@ class _NestingState(object):
|
|||
return classinfo
|
||||
return None
|
||||
|
||||
def CheckClassFinished(self, filename, error):
|
||||
"""Checks that all classes have been completely parsed.
|
||||
def CheckCompletedBlocks(self, filename, error):
|
||||
"""Checks that all classes and namespaces have been completely parsed.
|
||||
|
||||
Call this when all lines in a file have been processed.
|
||||
Args:
|
||||
|
@ -1883,11 +2050,15 @@ class _NestingState(object):
|
|||
error(filename, obj.starting_linenum, 'build/class', 5,
|
||||
'Failed to find complete declaration of class %s' %
|
||||
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,
|
||||
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
|
||||
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
|
||||
# they'll never need to wrap.
|
||||
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
|
||||
# Ignore pointers/references to functions.
|
||||
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
|
||||
|
@ -2044,7 +2215,7 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
|||
'Extra space after (')
|
||||
if (Search(r'\w\s+\(', 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,
|
||||
'Extra space before ( in function call')
|
||||
# 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."')
|
||||
|
||||
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 != '':
|
||||
error(filename, linenum, 'whitespace/todo', 2,
|
||||
'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.
|
||||
"""
|
||||
|
||||
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]
|
||||
|
||||
# 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:
|
||||
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
|
||||
# chain, like this:
|
||||
# if (condition1) {
|
||||
|
@ -2421,7 +2596,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||
and Match(r'\s*}', next_line)
|
||||
and next_line.find('} else ') == -1):
|
||||
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)
|
||||
if matched:
|
||||
|
@ -2432,7 +2608,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
|||
commentpos = line.find('//')
|
||||
if commentpos != -1:
|
||||
# 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) -
|
||||
line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
|
||||
# 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
|
||||
# comma is another comma, since the only time when that happens is
|
||||
# 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,
|
||||
'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
|
||||
# braces. And since you should never have braces at the beginning of a line,
|
||||
# this is an easy test.
|
||||
if Search(r'[^ ({]{', line):
|
||||
error(filename, linenum, 'whitespace/braces', 5,
|
||||
'Missing space before {')
|
||||
match = Match(r'^(.*[^ ({]){', line)
|
||||
if match:
|
||||
# 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.
|
||||
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
|
||||
|
||||
if Match(r'\s*{\s*$', line):
|
||||
# We allow an open brace to start a line in the case where someone
|
||||
# is using braces in a block to explicitly create a new scope,
|
||||
# which is commonly used to control the lifetime of
|
||||
# stack-allocated variables. We don't detect this perfectly: we
|
||||
# just don't complain if the last non-whitespace character on the
|
||||
# previous non-blank line is ',', ';', ':', '{', or '}', or if the
|
||||
# We allow an open brace to start a line in the case where someone is using
|
||||
# braces in a block to explicitly create a new scope, which is commonly used
|
||||
# to control the lifetime of stack-allocated variables. Braces are also
|
||||
# used for brace initializers inside function calls. We don't detect this
|
||||
# perfectly: we just don't complain if the last non-whitespace character on
|
||||
# the previous non-blank line is ',', ';', ':', '(', '{', or '}', or if the
|
||||
# previous line starts a preprocessor block.
|
||||
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)):
|
||||
error(filename, linenum, 'whitespace/braces', 4,
|
||||
'{ 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,
|
||||
'do/while clauses should not be on a single line')
|
||||
|
||||
# Braces shouldn't be followed by a ; unless they're defining a struct
|
||||
# or initializing an array.
|
||||
# We can't tell in general, but we can for some common cases.
|
||||
prevlinenum = linenum
|
||||
while True:
|
||||
(prevline, prevlinenum) = GetPreviousNonBlankLine(clean_lines, prevlinenum)
|
||||
if Match(r'\s+{.*}\s*;', line) and not prevline.count(';'):
|
||||
line = prevline + line
|
||||
else:
|
||||
break
|
||||
if (Search(r'{.*}\s*;', line) and
|
||||
line.count('{') == line.count('}') and
|
||||
not Search(r'struct|union|class|enum|\s*=\s*{', line)):
|
||||
error(filename, linenum, 'readability/braces', 4,
|
||||
"You don't need a ; after a }")
|
||||
# Block bodies should not be followed by a semicolon. Due to C++11
|
||||
# brace initialization, there are more places where semicolons are
|
||||
# required than not, so we use a whitelist approach to check these
|
||||
# rather than a blacklist. These are the places where "};" should
|
||||
# be replaced by just "}":
|
||||
# 1. Some flavor of block following closing parenthesis:
|
||||
# for (;;) {};
|
||||
# while (...) {};
|
||||
# switch (...) {};
|
||||
# Function(...) {};
|
||||
# if (...) {};
|
||||
# if (...) else if (...) {};
|
||||
#
|
||||
# 2. else block:
|
||||
# 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):
|
||||
|
@ -2876,7 +3191,7 @@ def CheckCheck(filename, clean_lines, linenum, error):
|
|||
if token == '(':
|
||||
# Parenthesized operand
|
||||
expression = matched.group(2)
|
||||
end = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
|
||||
(end, _) = FindEndOfExpressionInLine(expression, 0, 1, '(', ')')
|
||||
if end < 0:
|
||||
return # Unmatched parenthesis
|
||||
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.
|
||||
"""
|
||||
|
||||
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]
|
||||
|
||||
if line.find('\t') != -1:
|
||||
|
@ -3287,7 +3605,7 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
|||
|
||||
|
||||
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
|
||||
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.
|
||||
#
|
||||
# Supports nested templates up to 2 levels deep using this messy pattern:
|
||||
# < (?: < (?: < [^<>]*
|
||||
# >
|
||||
# | [^<>] )*
|
||||
# >
|
||||
# | [^<>] )*
|
||||
# >
|
||||
_RE_PATTERN_IDENT = r'[_a-zA-Z]\w*' # =~ [[:alpha:]][[:alnum:]]*
|
||||
_RE_PATTERN_TYPE = (
|
||||
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'.
|
||||
_RE_PATTERN_REF_PARAM = re.compile(
|
||||
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)
|
||||
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.
|
||||
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.
|
||||
# Function template specialization looks like: "string foo<Type>(...".
|
||||
# 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,
|
||||
'For a static/global string constant, use a C style string instead: '
|
||||
'"%schar %s[]".' %
|
||||
|
@ -3668,26 +4006,46 @@ def CheckForNonConstReference(filename, clean_lines, linenum,
|
|||
if '&' not in line:
|
||||
return
|
||||
|
||||
# Long type names may be broken across multiple lines, with the
|
||||
# newline before or after the scope resolution operator. If we
|
||||
# detected a type split across two lines, join the previous line to
|
||||
# current line so that we can match const references accordingly.
|
||||
# Long type names may be broken across multiple lines, usually in one
|
||||
# of these forms:
|
||||
# LongType
|
||||
# ::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
|
||||
# arbitrary number of lines would be expensive. If you have a type
|
||||
# that spans more than 2 lines, please use a typedef.
|
||||
if linenum > 1:
|
||||
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 = Search(r'\b((?:const\s*)?(?:\w|::)+\w)\s*$',
|
||||
previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+[\w<>])\s*$',
|
||||
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 = Search(r'\b((?:const\s*)?(?:\w|::)+::)\s*$',
|
||||
previous = Search(r'\b((?:const\s*)?(?:[\w<>]|::)+::)\s*$',
|
||||
clean_lines.elided[linenum - 1])
|
||||
if previous:
|
||||
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
|
||||
# found in the following places:
|
||||
|
@ -3737,7 +4095,8 @@ def CheckForNonConstReference(filename, clean_lines, linenum,
|
|||
if not Match(_RE_PATTERN_CONST_REF_PARAM, parameter):
|
||||
error(filename, linenum, 'runtime/references', 2,
|
||||
'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,
|
||||
|
@ -4084,8 +4443,7 @@ def CheckMakePairUsesDeduction(filename, clean_lines, linenum, error):
|
|||
linenum: The number of the line to check.
|
||||
error: The function to call with any errors found.
|
||||
"""
|
||||
raw = clean_lines.raw_lines
|
||||
line = raw[linenum]
|
||||
line = clean_lines.elided[linenum]
|
||||
match = _RE_PATTERN_EXPLICIT_MAKEPAIR.search(line)
|
||||
if match:
|
||||
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)
|
||||
CheckForNonStandardConstructs(filename, clean_lines, line,
|
||||
nesting_state, error)
|
||||
CheckVlogArguments(filename, clean_lines, line, error)
|
||||
CheckPosixThreading(filename, clean_lines, line, error)
|
||||
CheckInvalidIncrement(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,
|
||||
include_state, function_state, nesting_state, error,
|
||||
extra_check_functions)
|
||||
nesting_state.CheckClassFinished(filename, error)
|
||||
nesting_state.CheckCompletedBlocks(filename, error)
|
||||
|
||||
CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
|
||||
|
||||
# We check here rather than inside ProcessLine so that we see raw
|
||||
# lines rather than "cleaned" lines.
|
||||
CheckForUnicodeReplacementCharacters(filename, lines, error)
|
||||
CheckForBadCharacters(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
|
||||
# should rely on the extension.
|
||||
if (filename != '-' and file_extension != 'cc' and file_extension != 'h'
|
||||
and file_extension != 'cpp'):
|
||||
sys.stderr.write('Ignoring %s; not a .cc or .h file\n' % filename)
|
||||
valid_extensions = ['cc', 'h', 'cpp', 'cu', 'cuh']
|
||||
if filename != '-' and file_extension not in valid_extensions:
|
||||
sys.stderr.write('Ignoring %s; not a valid file name '
|
||||
'(.cc, .h, .cpp, .cu, .cuh)\n' % filename)
|
||||
else:
|
||||
ProcessFileData(filename, file_extension, lines, Error,
|
||||
extra_check_functions)
|
||||
|
|
|
@ -139,7 +139,7 @@ class CpplintTestBase(unittest.TestCase):
|
|||
error_collector)
|
||||
cpplint.CheckForNonStandardConstructs('foo.h', lines, i,
|
||||
nesting_state, error_collector)
|
||||
nesting_state.CheckClassFinished('foo.h', error_collector)
|
||||
nesting_state.CheckCompletedBlocks('foo.h', error_collector)
|
||||
return error_collector.Results()
|
||||
|
||||
# Similar to PerformMultiLineLint, but calls CheckLanguage instead of
|
||||
|
@ -230,13 +230,13 @@ class CpplintTestBase(unittest.TestCase):
|
|||
self.assertEquals(
|
||||
start_errors,
|
||||
error_collector.Results().count(
|
||||
'Blank line at the start of a code block. Is this needed?'
|
||||
' [whitespace/blank_line] [2]'))
|
||||
'Redundant blank line at the start of a code block '
|
||||
'should be deleted. [whitespace/blank_line] [2]'))
|
||||
self.assertEquals(
|
||||
end_errors,
|
||||
error_collector.Results().count(
|
||||
'Blank line at the end of a code block. Is this needed?'
|
||||
' [whitespace/blank_line] [3]'))
|
||||
'Redundant blank line at the end of a code block '
|
||||
'should be deleted. [whitespace/blank_line] [3]'))
|
||||
|
||||
|
||||
class CpplintTest(CpplintTestBase):
|
||||
|
@ -832,10 +832,55 @@ class CpplintTest(CpplintTestBase):
|
|||
self.assertEqual('f(a, b);',
|
||||
cpplint.CleanseComments('f(a, /* name */ b);'))
|
||||
self.assertEqual('f(a, b);',
|
||||
cpplint.CleanseComments('f(a /* name */, b);'))
|
||||
cpplint.CleanseComments('f(a /* name */, b);'))
|
||||
self.assertEqual('f(a, 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):
|
||||
# missing explicit is bad
|
||||
self.TestMultiLineLint(
|
||||
|
@ -859,8 +904,8 @@ class CpplintTest(CpplintTestBase):
|
|||
def testMultilineStrings(self):
|
||||
multiline_string_error_message = (
|
||||
'Multi-line string ("...") found. This lint script doesn\'t '
|
||||
'do well with such strings, and may give bogus warnings. They\'re '
|
||||
'ugly and unnecessary, and you should use concatenation instead".'
|
||||
'do well with such strings, and may give bogus warnings. '
|
||||
'Use C++11 raw strings or concatenation instead.'
|
||||
' [readability/multiline_string] [5]')
|
||||
|
||||
file_path = 'mydir/foo.cc'
|
||||
|
@ -1151,6 +1196,31 @@ class CpplintTest(CpplintTestBase):
|
|||
' for improved thread safety.'
|
||||
' [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).
|
||||
def testFormatStrings(self):
|
||||
self.TestLint('printf("foo")', '')
|
||||
|
@ -1632,6 +1702,11 @@ class CpplintTest(CpplintTestBase):
|
|||
self.TestLint('T& r = v', '')
|
||||
self.TestLint('static_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
|
||||
# state to reproduce as opposed to just TestLint.
|
||||
|
@ -1662,16 +1737,21 @@ class CpplintTest(CpplintTestBase):
|
|||
' Inner& const_x,',
|
||||
' const Outer',
|
||||
' ::Inner& const_y,',
|
||||
' const Outer<',
|
||||
' int>::Inner& const_z,',
|
||||
' Outer::',
|
||||
' Inner& nonconst_x,',
|
||||
' Outer',
|
||||
' ::Inner& nonconst_y) {',
|
||||
' ::Inner& nonconst_y,',
|
||||
' Outer<',
|
||||
' int>::Inner& nonconst_z) {',
|
||||
'}',
|
||||
''],
|
||||
error_collector)
|
||||
self.assertEquals(
|
||||
[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())
|
||||
|
||||
def testBraceAtBeginOfLine(self):
|
||||
|
@ -1705,6 +1785,14 @@ class CpplintTest(CpplintTestBase):
|
|||
'{ should almost always be at the end of the previous line'
|
||||
' [whitespace/braces] [4]'))
|
||||
|
||||
self.TestMultiLineLint(
|
||||
"""
|
||||
foo(
|
||||
{
|
||||
loooooooooooooooong_value,
|
||||
});""",
|
||||
'')
|
||||
|
||||
def testMismatchingSpacesInParens(self):
|
||||
self.TestLint('if (foo ) {', 'Mismatching spaces inside () in if'
|
||||
' [whitespace/parens] [5]')
|
||||
|
@ -1758,6 +1846,7 @@ class CpplintTest(CpplintTestBase):
|
|||
self.TestLint('typedef foo (*foo12bar_)(foo)', '')
|
||||
self.TestLint('typedef foo (Foo::*bar)(foo)', '')
|
||||
self.TestLint('foo (Foo::*bar)(', '')
|
||||
self.TestLint('foo (x::y::*z)(', '')
|
||||
self.TestLint('foo (Foo::bar)(',
|
||||
'Extra space before ( in function call'
|
||||
' [whitespace/parens] [4]')
|
||||
|
@ -1769,6 +1858,13 @@ class CpplintTest(CpplintTestBase):
|
|||
self.TestLint('char (*p)[sizeof(foo)] = &foo', '')
|
||||
self.TestLint('char (&ref)[sizeof(foo)] = &foo', '')
|
||||
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):
|
||||
self.TestLint('if (foo){', 'Missing space before {'
|
||||
|
@ -1781,11 +1877,63 @@ class CpplintTest(CpplintTestBase):
|
|||
def testSemiColonAfterBraces(self):
|
||||
self.TestLint('if (cond) {};',
|
||||
'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('struct X {};', '')
|
||||
self.TestLint('union {} x;', '')
|
||||
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):
|
||||
self.TestLint('}else {', 'Missing space before else'
|
||||
|
@ -1940,6 +2088,8 @@ class CpplintTest(CpplintTestBase):
|
|||
'For a static/global string constant, use a C style '
|
||||
'string instead: "char Foo::bar[]".'
|
||||
' [runtime/string] [4]')
|
||||
self.TestLint('string Foo::bar() {}', '')
|
||||
self.TestLint('string Foo::operator*() {}', '')
|
||||
# Rare case.
|
||||
self.TestLint('string foo("foobar");',
|
||||
'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')).
|
||||
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):
|
||||
self.assert_(cpplint.IsBlankLine(''))
|
||||
self.assert_(cpplint.IsBlankLine(' '))
|
||||
|
@ -2150,7 +2324,7 @@ class CpplintTest(CpplintTestBase):
|
|||
'}'],
|
||||
error_collector)
|
||||
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]'))
|
||||
|
||||
def testAllowBlankLineBeforeIfElseChain(self):
|
||||
|
@ -2167,7 +2341,7 @@ class CpplintTest(CpplintTestBase):
|
|||
'}'],
|
||||
error_collector)
|
||||
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]'))
|
||||
|
||||
def testBlankLineBeforeSectionKeyword(self):
|
||||
|
@ -2335,6 +2509,8 @@ class CpplintTest(CpplintTestBase):
|
|||
'Missing space after , [whitespace/comma] [3]'])
|
||||
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,, 2)', '')
|
||||
|
||||
|
@ -2542,6 +2718,11 @@ class CpplintTest(CpplintTestBase):
|
|||
'class Foo {',
|
||||
'Failed to find complete declaration of class Foo'
|
||||
' [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.
|
||||
self.TestMultiLineLint(
|
||||
'class Foo;',
|
||||
|
@ -3256,6 +3437,34 @@ class OrderOfIncludesTest(CpplintTestBase):
|
|||
'Include "a/b.h" not in alphabetical '
|
||||
'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):
|
||||
def setUp(self):
|
||||
|
|
Loading…
Reference in New Issue
Block a user