Update cpplint.py to #242:

- Check indentation of public/protected/private keywords.
- Remove RTTI warning.
- Silence warning about multiple inheritance from global namespace.
- Copy ctors don't need "explicit".
- Understand "const char* const&" as a const reference.
- Remove runtime/sizeof.
- Recognize the "catch" keyword.
- List C++11 headers
- Allow sscanf()
- Allow for one extra level of nesting in template class decls.
- False positive for semicolons after single-line nameless unions.

R=mark@chromium.org

Review URL: https://codereview.appspot.com/15740044
This commit is contained in:
erg@google.com 2013-10-25 17:39:45 +00:00
parent 7b24563e08
commit fd5da63478
2 changed files with 582 additions and 224 deletions

387
cpplint/cpplint.py vendored
View File

@ -200,8 +200,6 @@ _ERROR_CATEGORIES = [
'runtime/printf', 'runtime/printf',
'runtime/printf_format', 'runtime/printf_format',
'runtime/references', 'runtime/references',
'runtime/rtti',
'runtime/sizeof',
'runtime/string', 'runtime/string',
'runtime/threadsafe_fn', 'runtime/threadsafe_fn',
'whitespace/blank_line', 'whitespace/blank_line',
@ -213,7 +211,6 @@ _ERROR_CATEGORIES = [
'whitespace/ending_newline', 'whitespace/ending_newline',
'whitespace/forcolon', 'whitespace/forcolon',
'whitespace/indent', 'whitespace/indent',
'whitespace/labels',
'whitespace/line_length', 'whitespace/line_length',
'whitespace/newline', 'whitespace/newline',
'whitespace/operators', 'whitespace/operators',
@ -233,36 +230,143 @@ _DEFAULT_FILTERS = ['-build/include_alpha']
# decided those were OK, as long as they were in UTF-8 and didn't represent # decided those were OK, as long as they were in UTF-8 and didn't represent
# hard-coded international strings, which belong in a separate i18n file. # hard-coded international strings, which belong in a separate i18n file.
# Headers that we consider STL headers.
_STL_HEADERS = frozenset([
'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception',
'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set',
'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new',
'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack',
'stl_alloc.h', 'stl_relops.h', 'type_traits.h',
'utility', 'vector', 'vector.h',
])
# C++ headers
# Non-STL C++ system headers.
_CPP_HEADERS = frozenset([ _CPP_HEADERS = frozenset([
'algo.h', 'builtinbuf.h', 'bvector.h', 'cassert', 'cctype', # Legacy
'cerrno', 'cfloat', 'ciso646', 'climits', 'clocale', 'cmath', 'algobase.h',
'complex', 'complex.h', 'csetjmp', 'csignal', 'cstdarg', 'cstddef', 'algo.h',
'cstdio', 'cstdlib', 'cstring', 'ctime', 'cwchar', 'cwctype', 'alloc.h',
'defalloc.h', 'deque.h', 'editbuf.h', 'exception', 'fstream', 'builtinbuf.h',
'fstream.h', 'hashtable.h', 'heap.h', 'indstream.h', 'iomanip', 'bvector.h',
'iomanip.h', 'ios', 'iosfwd', 'iostream', 'iostream.h', 'istream', 'complex.h',
'istream.h', 'iterator.h', 'limits', 'map.h', 'multimap.h', 'multiset.h', 'defalloc.h',
'numeric', 'ostream', 'ostream.h', 'parsestream.h', 'pfstream.h', 'deque.h',
'PlotFile.h', 'procbuf.h', 'pthread_alloc.h', 'rope', 'rope.h', 'editbuf.h',
'ropeimpl.h', 'SFile.h', 'slist', 'slist.h', 'stack.h', 'stdexcept', 'fstream.h',
'stdiostream.h', 'streambuf', 'streambuf.h', 'stream.h', 'strfile.h', 'function.h',
'string', 'strstream', 'strstream.h', 'tempbuf.h', 'tree.h', 'typeinfo', 'hash_map',
'hash_map.h',
'hash_set',
'hash_set.h',
'hashtable.h',
'heap.h',
'indstream.h',
'iomanip.h',
'iostream.h',
'istream.h',
'iterator.h',
'list.h',
'map.h',
'multimap.h',
'multiset.h',
'ostream.h',
'pair.h',
'parsestream.h',
'pfstream.h',
'procbuf.h',
'pthread_alloc',
'pthread_alloc.h',
'rope',
'rope.h',
'ropeimpl.h',
'set.h',
'slist',
'slist.h',
'stack.h',
'stdiostream.h',
'stl_alloc.h',
'stl_relops.h',
'streambuf.h',
'stream.h',
'strfile.h',
'strstream.h',
'tempbuf.h',
'tree.h',
'type_traits.h',
'vector.h',
# 17.6.1.2 C++ library headers
'algorithm',
'array',
'atomic',
'bitset',
'chrono',
'codecvt',
'complex',
'condition_variable',
'deque',
'exception',
'forward_list',
'fstream',
'functional',
'future',
'initializer_list',
'iomanip',
'ios',
'iosfwd',
'iostream',
'istream',
'iterator',
'limits',
'list',
'locale',
'map',
'memory',
'mutex',
'new',
'numeric',
'ostream',
'queue',
'random',
'ratio',
'regex',
'set',
'sstream',
'stack',
'stdexcept',
'streambuf',
'string',
'strstream',
'system_error',
'thread',
'tuple',
'typeindex',
'typeinfo',
'type_traits',
'unordered_map',
'unordered_set',
'utility',
'valarray', 'valarray',
'vector',
# 17.6.1.2 C++ headers for C library facilities
'cassert',
'ccomplex',
'cctype',
'cerrno',
'cfenv',
'cfloat',
'cinttypes',
'ciso646',
'climits',
'clocale',
'cmath',
'csetjmp',
'csignal',
'cstdalign',
'cstdarg',
'cstdbool',
'cstddef',
'cstdint',
'cstdio',
'cstdlib',
'cstring',
'ctgmath',
'ctime',
'cuchar',
'cwchar',
'cwctype',
]) ])
# Assertion macros. These are defined in base/logging.h and # Assertion macros. These are defined in base/logging.h and
# testing/base/gunit.h. Note that the _M versions need to come first # testing/base/gunit.h. Note that the _M versions need to come first
# for substring matching to work. # for substring matching to work.
@ -416,6 +520,24 @@ def Match(pattern, s):
return _regexp_compile_cache[pattern].match(s) return _regexp_compile_cache[pattern].match(s)
def ReplaceAll(pattern, rep, s):
"""Replaces instances of pattern in a string with a replacement.
The compiled regex is kept in a cache shared by Match and Search.
Args:
pattern: regex pattern
rep: replacement text
s: search string
Returns:
string with replacements made (or original string if no replacements)
"""
if pattern not in _regexp_compile_cache:
_regexp_compile_cache[pattern] = sre_compile.compile(pattern)
return _regexp_compile_cache[pattern].sub(rep, s)
def Search(pattern, s): def Search(pattern, s):
"""Searches the string for the pattern, caching the compiled regexp.""" """Searches the string for the pattern, caching the compiled regexp."""
if not pattern in _regexp_compile_cache: if not pattern in _regexp_compile_cache:
@ -464,6 +586,9 @@ class _IncludeState(dict):
# The path of last found header. # The path of last found header.
self._last_header = '' self._last_header = ''
def SetLastHeader(self, header_path):
self._last_header = header_path
def CanonicalizeAlphabeticalOrder(self, header_path): def CanonicalizeAlphabeticalOrder(self, header_path):
"""Returns a path canonicalized for alphabetical comparison. """Returns a path canonicalized for alphabetical comparison.
@ -479,19 +604,25 @@ class _IncludeState(dict):
""" """
return header_path.replace('-inl.h', '.h').replace('-', '_').lower() return header_path.replace('-inl.h', '.h').replace('-', '_').lower()
def IsInAlphabeticalOrder(self, header_path): def IsInAlphabeticalOrder(self, clean_lines, linenum, header_path):
"""Check if a header is in alphabetical order with the previous header. """Check if a header is in alphabetical order with the previous header.
Args: Args:
header_path: Header to be checked. clean_lines: A CleansedLines instance containing the file.
linenum: The number of the line to check.
header_path: Canonicalized header to be checked.
Returns: Returns:
Returns true if the header is in alphabetical order. Returns true if the header is in alphabetical order.
""" """
canonical_header = self.CanonicalizeAlphabeticalOrder(header_path) # If previous section is different from current section, _last_header will
if self._last_header > canonical_header: # be reset to empty string, so it's always less than current header.
#
# If previous line was a blank line, assume that the headers are
# intentionally sorted the way they are.
if (self._last_header > header_path and
not Match(r'^\s*$', clean_lines.elided[linenum - 1])):
return False return False
self._last_header = canonical_header
return True return True
def CheckNextIncludeOrder(self, header_type): def CheckNextIncludeOrder(self, header_type):
@ -1401,8 +1532,18 @@ class _ClassInfo(_BlockInfo):
self.is_derived = False self.is_derived = False
if class_or_struct == 'struct': if class_or_struct == 'struct':
self.access = 'public' self.access = 'public'
self.is_struct = True
else: else:
self.access = 'private' self.access = 'private'
self.is_struct = False
# Remember initial indentation level for this class. Using raw_lines here
# instead of elided to account for leading comments like http://go/abjhm
initial_indent = Match(r'^( *)\S', clean_lines.raw_lines[linenum])
if initial_indent:
self.class_indent = len(initial_indent.group(1))
else:
self.class_indent = 0
# Try to find the end of the class. This will be confused by things like: # Try to find the end of the class. This will be confused by things like:
# class A { # class A {
@ -1423,6 +1564,19 @@ class _ClassInfo(_BlockInfo):
if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]): if Search('(^|[^:]):($|[^:])', clean_lines.elided[linenum]):
self.is_derived = True self.is_derived = True
def CheckEnd(self, filename, clean_lines, linenum, error):
# Check that closing brace is aligned with beginning of the class.
# Only do this if the closing brace is indented by only whitespaces.
# This means we will not check single-line class definitions.
indent = Match(r'^( *)\}', clean_lines.elided[linenum])
if indent and len(indent.group(1)) != self.class_indent:
if self.is_struct:
parent = 'struct ' + self.name
else:
parent = 'class ' + self.name
error(filename, linenum, 'whitespace/indent', 3,
'Closing brace should be aligned with beginning of %s' % parent)
class _NamespaceInfo(_BlockInfo): class _NamespaceInfo(_BlockInfo):
"""Stores information about a namespace.""" """Stores information about a namespace."""
@ -1661,8 +1815,8 @@ class _NestingState(object):
# To avoid these cases, we ignore classes that are followed by '=' or '>' # To avoid these cases, we ignore classes that are followed by '=' or '>'
class_decl_match = Match( class_decl_match = Match(
r'\s*(template\s*<[\w\s<>,:]*>\s*)?' r'\s*(template\s*<[\w\s<>,:]*>\s*)?'
'(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)' r'(class|struct)\s+([A-Z_]+\s+)*(\w+(?:::\w+)*)'
'(([^=>]|<[^<>]*>)*)$', line) r'(([^=>]|<[^<>]*>|<[^<>]*<[^<>]*>\s*>)*)$', line)
if (class_decl_match and if (class_decl_match and
(not self.stack or self.stack[-1].open_parentheses == 0)): (not self.stack or self.stack[-1].open_parentheses == 0)):
self.stack.append(_ClassInfo( self.stack.append(_ClassInfo(
@ -1677,9 +1831,30 @@ class _NestingState(object):
# Update access control if we are inside a class/struct # Update access control if we are inside a class/struct
if self.stack and isinstance(self.stack[-1], _ClassInfo): if self.stack and isinstance(self.stack[-1], _ClassInfo):
access_match = Match(r'\s*(public|private|protected)\s*:', line) classinfo = self.stack[-1]
access_match = Match(
r'^(.*)\b(public|private|protected|signals)(\s+(?:slots\s*)?)?'
r':(?:[^:]|$)',
line)
if access_match: if access_match:
self.stack[-1].access = access_match.group(1) classinfo.access = access_match.group(2)
# Check that access keywords are indented +1 space. Skip this
# check if the keywords are not preceded by whitespaces, examples:
# http://go/cfudb + http://go/vxnkk
indent = access_match.group(1)
if (len(indent) != classinfo.class_indent + 1 and
Match(r'^\s*$', indent)):
if classinfo.is_struct:
parent = 'struct ' + classinfo.name
else:
parent = 'class ' + classinfo.name
slots = ''
if access_match.group(3):
slots = access_match.group(3)
error(filename, linenum, 'whitespace/indent', 3,
'%s%s: should be indented +1 space inside %s' % (
access_match.group(2), slots, parent))
# Consume braces or semicolons from what's left of the line # Consume braces or semicolons from what's left of the line
while True: while True:
@ -1848,8 +2023,8 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum,
line) line)
if (args and if (args and
args.group(1) != 'void' and args.group(1) != 'void' and
not Match(r'(const\s+)?%s\s*(?:<\w+>\s*)?&' % re.escape(base_classname), not Match(r'(const\s+)?%s(\s+const)?\s*(?:<\w+>\s*)?&'
args.group(1).strip())): % re.escape(base_classname), args.group(1).strip())):
error(filename, linenum, 'runtime/explicit', 5, error(filename, linenum, 'runtime/explicit', 5,
'Single-argument constructors should be marked explicit.') 'Single-argument constructors should be marked explicit.')
@ -1892,7 +2067,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|delete)\b', fncall) and not Search(r'\b(if|for|while|switch|return|delete|catch)\b', fncall) and
# Ignore pointers/references to functions. # Ignore pointers/references to functions.
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
# Ignore pointers/references to arrays. # Ignore pointers/references to arrays.
@ -2635,7 +2810,7 @@ def CheckBraces(filename, clean_lines, linenum, error):
break break
if (Search(r'{.*}\s*;', line) and if (Search(r'{.*}\s*;', line) and
line.count('{') == line.count('}') and line.count('{') == line.count('}') and
not Search(r'struct|class|enum|\s*=\s*{', line)): not Search(r'struct|union|class|enum|\s*=\s*{', line)):
error(filename, linenum, 'readability/braces', 4, error(filename, linenum, 'readability/braces', 4,
"You don't need a ; after a }") "You don't need a ; after a }")
@ -2833,21 +3008,12 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
if line and line[-1].isspace(): if line and line[-1].isspace():
error(filename, linenum, 'whitespace/end_of_line', 4, error(filename, linenum, 'whitespace/end_of_line', 4,
'Line ends in whitespace. Consider deleting these extra spaces.') 'Line ends in whitespace. Consider deleting these extra spaces.')
# There are certain situations we allow one space, notably for labels # There are certain situations we allow one space, notably for section labels
elif ((initial_spaces == 1 or initial_spaces == 3) and elif ((initial_spaces == 1 or initial_spaces == 3) and
not Match(r'\s*\w+\s*:\s*$', cleansed_line)): not Match(r'\s*\w+\s*:\s*$', cleansed_line)):
error(filename, linenum, 'whitespace/indent', 3, error(filename, linenum, 'whitespace/indent', 3,
'Weird number of spaces at line-start. ' 'Weird number of spaces at line-start. '
'Are you using a 2-space indent?') 'Are you using a 2-space indent?')
# Labels should always be indented at least one space.
elif not initial_spaces and line[:2] != '//' and Search(r'[^:]:\s*$',
line):
error(filename, linenum, 'whitespace/labels', 4,
'Labels should always be indented at least one space. '
'If this is a member-initializer list in a constructor or '
'the base class list in a class definition, the colon should '
'be on the following line.')
# Check if the line is a header guard. # Check if the line is a header guard.
is_header_guard = False is_header_guard = False
@ -2980,8 +3146,7 @@ def _ClassifyInclude(fileinfo, include, is_system):
""" """
# This is a list of all standard c++ header files, except # This is a list of all standard c++ header files, except
# those already checked for above. # those already checked for above.
is_stl_h = include in _STL_HEADERS is_cpp_h = include in _CPP_HEADERS
is_cpp_h = is_stl_h or include in _CPP_HEADERS
if is_system: if is_system:
if is_cpp_h: if is_cpp_h:
@ -3069,9 +3234,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
error(filename, linenum, 'build/include_order', 4, error(filename, linenum, 'build/include_order', 4,
'%s. Should be: %s.h, c system, c++ system, other.' % '%s. Should be: %s.h, c system, c++ system, other.' %
(error_message, fileinfo.BaseName())) (error_message, fileinfo.BaseName()))
if not include_state.IsInAlphabeticalOrder(include): canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
if not include_state.IsInAlphabeticalOrder(
clean_lines, linenum, canonical_include):
error(filename, linenum, 'build/include_alpha', 4, error(filename, linenum, 'build/include_alpha', 4,
'Include "%s" not in alphabetical order' % include) 'Include "%s" not in alphabetical order' % include)
include_state.SetLastHeader(canonical_include)
# Look for any of the stream classes that are part of standard C++. # Look for any of the stream classes that are part of standard C++.
match = _RE_PATTERN_INCLUDE.match(line) match = _RE_PATTERN_INCLUDE.match(line)
@ -3140,8 +3308,24 @@ def _GetTextInside(text, start_pattern):
return text[start_position:position - 1] return text[start_position:position - 1]
def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, # Patterns for matching call-by-reference parameters.
error): _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+)?)?')
# A call-by-reference parameter ends with '& identifier'.
_RE_PATTERN_REF_PARAM = re.compile(
r'(' + _RE_PATTERN_TYPE + r'(?:\s*(?:\bconst\b|[*]))*\s*'
r'&\s*' + _RE_PATTERN_IDENT + r')\s*(?:=[^,()]+)?[,)]')
# A call-by-const-reference parameter either ends with 'const& identifier'
# or looks like 'const type& identifier' when 'type' is atomic.
_RE_PATTERN_CONST_REF_PARAM = (
r'(?:.*\s*\bconst\s*&\s*' + _RE_PATTERN_IDENT +
r'|const\s+' + _RE_PATTERN_TYPE + r'\s*&\s*' + _RE_PATTERN_IDENT + r')')
def CheckLanguage(filename, clean_lines, linenum, file_extension,
include_state, nesting_state, error):
"""Checks rules from the 'C++ language rules' section of cppguide.html. """Checks rules from the 'C++ language rules' section of cppguide.html.
Some of these rules are hard to test (function overloading, using Some of these rules are hard to test (function overloading, using
@ -3153,6 +3337,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state,
linenum: The number of the line to check. linenum: The number of the line to check.
file_extension: The extension (without the dot) of the filename. file_extension: The extension (without the dot) of the filename.
include_state: An _IncludeState instance in which the headers are inserted. include_state: An _IncludeState instance in which the headers are inserted.
nesting_state: A _NestingState instance which maintains information about
the current stack of nested blocks being parsed.
error: The function to call with any errors found. error: The function to call with any errors found.
""" """
# If the line is empty or consists of entirely a comment, no need to # If the line is empty or consists of entirely a comment, no need to
@ -3179,30 +3365,56 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state,
# TODO(unknown): figure out if they're using default arguments in fn proto. # TODO(unknown): figure out if they're using default arguments in fn proto.
# Check for non-const references in functions. This is tricky because & # Check for non-const references in function parameters. A single '&' may
# is also used to take the address of something. We allow <> for templates, # found in the following places:
# (ignoring whatever is between the braces) and : for classes. # inside expression: binary & for bitwise AND
# These are complicated re's. They try to capture the following: # inside expression: unary & for taking the address of something
# paren (for fn-prototype start), typename, &, varname. For the const # inside declarators: reference parameter
# version, we're willing for const to be before typename or after # We will exclude the first two cases by checking that we are not inside a
# Don't check the implementation on same line. # function body, including one that was just introduced by a trailing '{'.
fnline = line.split('{', 1)[0] # TODO(unknown): Doesn't account for preprocessor directives.
if (len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) > # TODO(unknown): Doesn't account for 'catch(Exception& e)' [rare].
len(re.findall(r'\([^()]*\bconst\s+(?:typename\s+)?(?:struct\s+)?' # TODO(unknown): Doesn't account for line breaks within declarators.
r'(?:[\w:]|<[^()]*>)+(\s?&|&\s?)\w+', fnline)) + check_params = False
len(re.findall(r'\([^()]*\b(?:[\w:]|<[^()]*>)+\s+const(\s?&|&\s?)[\w]+', if not nesting_state.stack:
fnline))): check_params = True # top level
elif (isinstance(nesting_state.stack[-1], _ClassInfo) or
isinstance(nesting_state.stack[-1], _NamespaceInfo)):
check_params = True # within class or namespace
elif Match(r'.*{\s*$', line):
if (len(nesting_state.stack) == 1 or
isinstance(nesting_state.stack[-2], _ClassInfo) or
isinstance(nesting_state.stack[-2], _NamespaceInfo)):
check_params = True # just opened global/class/namespace block
# We allow non-const references in a few standard places, like functions # We allow non-const references in a few standard places, like functions
# called "swap()" or iostream operators like "<<" or ">>". We also filter # called "swap()" or iostream operators like "<<" or ">>". Do not check
# out for loops, which lint otherwise mistakenly thinks are functions. # those function parameters.
if not Search( #
r'(for|swap|Swap|operator[<>][<>])\s*\(\s*' # We also accept & in static_assert, which looks like a function but
r'(?:(?:typename\s*)?[\w:]|<.*>)+\s*&', # it's actually a declaration expression.
fnline): whitelisted_functions = (r'(?:[sS]wap(?:<\w:+>)?|'
r'operator\s*[<>][<>]|'
r'static_assert|COMPILE_ASSERT'
r')\s*\(')
if Search(whitelisted_functions, line):
check_params = False
elif not Search(r'\S+\([^)]*$', line):
# Don't see a whitelisted function on this line. Actually we
# didn't see any function name on this line, so this is likely a
# multi-line parameter list. Try a bit harder to catch this case.
for i in xrange(2):
if (linenum > i and
Search(whitelisted_functions, clean_lines.elided[linenum - i - 1])):
check_params = False
break
if check_params:
decls = ReplaceAll(r'{[^}]*}', ' ', line) # exclude function body
for parameter in re.findall(_RE_PATTERN_REF_PARAM, decls):
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.') 'If so, make const or use a pointer: ' + parameter)
# Check to see if they're using an conversion function cast. # Check to see if they're using an conversion function cast.
# I just try to capture the most common basic types, though there are more. # I just try to capture the most common basic types, though there are more.
@ -3276,13 +3488,6 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state,
'"%schar %s[]".' % '"%schar %s[]".' %
(match.group(1), match.group(2))) (match.group(1), match.group(2)))
# Check that we're not using RTTI outside of testing code.
if Search(r'\bdynamic_cast<', line) and not _IsTestFilename(filename):
error(filename, linenum, 'runtime/rtti', 5,
'Do not use dynamic_cast<>. If you need to cast within a class '
"hierarchy, use static_cast<> to upcast. Google doesn't support "
'RTTI.')
if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line): if Search(r'\b([A-Za-z0-9_]*_)\(\1\)', line):
error(filename, linenum, 'runtime/init', 4, error(filename, linenum, 'runtime/init', 4,
'You seem to be initializing a member variable with itself.') 'You seem to be initializing a member variable with itself.')
@ -3324,10 +3529,6 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state,
error(filename, linenum, 'runtime/printf', 4, error(filename, linenum, 'runtime/printf', 4,
'Almost always, snprintf is better than %s' % match.group(1)) 'Almost always, snprintf is better than %s' % match.group(1))
if Search(r'\bsscanf\b', line):
error(filename, linenum, 'runtime/printf', 1,
'sscanf can be ok, but is slow and can overflow buffers.')
# Check if some verboten operator overloading is going on # Check if some verboten operator overloading is going on
# TODO(unknown): catch out-of-line unary operator&: # TODO(unknown): catch out-of-line unary operator&:
# class X {}; # class X {};
@ -3448,8 +3649,6 @@ def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
error): error):
"""Checks for a C-style cast by looking for the pattern. """Checks for a C-style cast by looking for the pattern.
This also handles sizeof(type) warnings, due to similarity of content.
Args: Args:
filename: The name of the current file. filename: The name of the current file.
linenum: The number of the line to check. linenum: The number of the line to check.
@ -3468,12 +3667,10 @@ def CheckCStyleCast(filename, linenum, line, raw_line, cast_type, pattern,
if not match: if not match:
return False return False
# e.g., sizeof(int) # Exclude lines with sizeof, since sizeof looks like a cast.
sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1]) sizeof_match = Match(r'.*sizeof\s*$', line[0:match.start(1) - 1])
if sizeof_match: if sizeof_match:
error(filename, linenum, 'runtime/sizeof', 1, return False
'Using sizeof(type). Use sizeof(varname) instead if possible')
return True
# operator++(int) and operator--(int) # operator++(int) and operator--(int)
if (line[0:match.start(1) - 1].endswith(' operator++') or if (line[0:match.start(1) - 1].endswith(' operator++') or
@ -3802,7 +3999,7 @@ def ProcessLine(filename, file_extension, clean_lines, line,
CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error) CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
CheckLanguage(filename, clean_lines, line, file_extension, include_state, CheckLanguage(filename, clean_lines, line, file_extension, include_state,
error) nesting_state, error)
CheckForNonStandardConstructs(filename, clean_lines, line, CheckForNonStandardConstructs(filename, clean_lines, line,
nesting_state, error) nesting_state, error)
CheckPosixThreading(filename, clean_lines, line, error) CheckPosixThreading(filename, clean_lines, line, error)

View File

@ -147,13 +147,14 @@ class CpplintTestBase(unittest.TestCase):
def PerformLanguageRulesCheck(self, file_name, code): def PerformLanguageRulesCheck(self, file_name, code):
error_collector = ErrorCollector(self.assert_) error_collector = ErrorCollector(self.assert_)
include_state = cpplint._IncludeState() include_state = cpplint._IncludeState()
nesting_state = cpplint._NestingState()
lines = code.split('\n') lines = code.split('\n')
cpplint.RemoveMultiLineComments(file_name, lines, error_collector) cpplint.RemoveMultiLineComments(file_name, lines, error_collector)
lines = cpplint.CleansedLines(lines) lines = cpplint.CleansedLines(lines)
ext = file_name[file_name.rfind('.') + 1:] ext = file_name[file_name.rfind('.') + 1:]
for i in xrange(lines.NumLines()): for i in xrange(lines.NumLines()):
cpplint.CheckLanguage(file_name, lines, i, ext, include_state, cpplint.CheckLanguage(file_name, lines, i, ext, include_state,
error_collector) nesting_state, error_collector)
return error_collector.Results() return error_collector.Results()
def PerformFunctionLengthsCheck(self, code): def PerformFunctionLengthsCheck(self, code):
@ -186,12 +187,13 @@ class CpplintTestBase(unittest.TestCase):
# First, build up the include state. # First, build up the include state.
error_collector = ErrorCollector(self.assert_) error_collector = ErrorCollector(self.assert_)
include_state = cpplint._IncludeState() include_state = cpplint._IncludeState()
nesting_state = cpplint._NestingState()
lines = code.split('\n') lines = code.split('\n')
cpplint.RemoveMultiLineComments(filename, lines, error_collector) cpplint.RemoveMultiLineComments(filename, lines, error_collector)
lines = cpplint.CleansedLines(lines) lines = cpplint.CleansedLines(lines)
for i in xrange(lines.NumLines()): for i in xrange(lines.NumLines()):
cpplint.CheckLanguage(filename, lines, i, '.h', include_state, cpplint.CheckLanguage(filename, lines, i, '.h', include_state,
error_collector) nesting_state, error_collector)
# We could clear the error_collector here, but this should # We could clear the error_collector here, but this should
# also be fine, since our IncludeWhatYouUse unittests do not # also be fine, since our IncludeWhatYouUse unittests do not
# have language problems. # have language problems.
@ -410,16 +412,6 @@ class CpplintTest(CpplintTestBase):
'Take the address before doing the cast, rather than after' 'Take the address before doing the cast, rather than after'
' [runtime/casting] [4]') ' [runtime/casting] [4]')
self.TestLint(
'int* x = &dynamic_cast<int *>(foo);',
['Are you taking an address of a cast? '
'This is dangerous: could be a temp var. '
'Take the address before doing the cast, rather than after'
' [runtime/casting] [4]',
'Do not use dynamic_cast<>. If you need to cast within a class '
'hierarchy, use static_cast<> to upcast. Google doesn\'t support '
'RTTI. [runtime/rtti] [5]'])
self.TestLint( self.TestLint(
'int* x = &reinterpret_cast<int *>(foo);', 'int* x = &reinterpret_cast<int *>(foo);',
'Are you taking an address of a cast? ' 'Are you taking an address of a cast? '
@ -444,20 +436,6 @@ class CpplintTest(CpplintTestBase):
'Foo::Foo(Bar r) : r_(r), l_(r_), ll_(l_) { }', 'Foo::Foo(Bar r) : r_(r), l_(r_), ll_(l_) { }',
'') '')
def testRuntimeRTTI(self):
statement = 'int* x = dynamic_cast<int*>(&foo);'
error_message = (
'Do not use dynamic_cast<>. If you need to cast within a class '
'hierarchy, use static_cast<> to upcast. Google doesn\'t support '
'RTTI. [runtime/rtti] [5]')
# dynamic_cast is disallowed in most files.
self.TestLanguageRulesCheck('foo.cc', statement, error_message)
self.TestLanguageRulesCheck('foo.h', statement, error_message)
# It is explicitly allowed in tests, however.
self.TestLanguageRulesCheck('foo_test.cc', statement, '')
self.TestLanguageRulesCheck('foo_unittest.cc', statement, '')
self.TestLanguageRulesCheck('foo_regtest.cc', statement, '')
# Test for unnamed arguments in a method. # Test for unnamed arguments in a method.
def testCheckForUnnamedParams(self): def testCheckForUnnamedParams(self):
message = ('All parameters should be named in a function' message = ('All parameters should be named in a function'
@ -571,17 +549,6 @@ class CpplintTest(CpplintTestBase):
'MockCallback<int(float, char)>', 'MockCallback<int(float, char)>',
'') '')
# Test sizeof(type) cases.
def testSizeofType(self):
self.TestLint(
'sizeof(int);',
'Using sizeof(type). Use sizeof(varname) instead if possible'
' [runtime/sizeof] [1]')
self.TestLint(
'sizeof(int *);',
'Using sizeof(type). Use sizeof(varname) instead if possible'
' [runtime/sizeof] [1]')
# Test false errors that happened with some include file names # Test false errors that happened with some include file names
def testIncludeFilenameFalseError(self): def testIncludeFilenameFalseError(self):
self.TestLint( self.TestLint(
@ -876,14 +843,16 @@ class CpplintTest(CpplintTestBase):
def testExplicitSingleArgumentConstructors(self): def testExplicitSingleArgumentConstructors(self):
# missing explicit is bad # missing explicit is bad
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(int f); Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# missing explicit is bad, even with whitespace # missing explicit is bad, even with whitespace
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo (int f); Foo (int f);
};""", };""",
['Extra space before ( in function call [whitespace/parens] [4]', ['Extra space before ( in function call [whitespace/parens] [4]',
@ -891,93 +860,107 @@ class CpplintTest(CpplintTestBase):
' [runtime/explicit] [5]']) ' [runtime/explicit] [5]'])
# missing explicit, with distracting comment, is still bad # missing explicit, with distracting comment, is still bad
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(int f); // simpler than Foo(blargh, blarg) Foo(int f); // simpler than Foo(blargh, blarg)
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# missing explicit, with qualified classname # missing explicit, with qualified classname
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Qualifier::AnotherOne::Foo { """
class Qualifier::AnotherOne::Foo {
Foo(int f); Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# missing explicit for inline constructors is bad as well # missing explicit for inline constructors is bad as well
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
inline Foo(int f); inline Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# structs are caught as well. # structs are caught as well.
self.TestMultiLineLint( self.TestMultiLineLint(
"""struct Foo { """
struct Foo {
Foo(int f); Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# Templatized classes are caught as well. # Templatized classes are caught as well.
self.TestMultiLineLint( self.TestMultiLineLint(
"""template<typename T> class Foo { """
template<typename T> class Foo {
Foo(int f); Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# inline case for templatized classes. # inline case for templatized classes.
self.TestMultiLineLint( self.TestMultiLineLint(
"""template<typename T> class Foo { """
template<typename T> class Foo {
inline Foo(int f); inline Foo(int f);
};""", };""",
'Single-argument constructors should be marked explicit.' 'Single-argument constructors should be marked explicit.'
' [runtime/explicit] [5]') ' [runtime/explicit] [5]')
# proper style is okay # proper style is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
explicit Foo(int f); explicit Foo(int f);
};""", };""",
'') '')
# two argument constructor is okay # two argument constructor is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(int f, int b); Foo(int f, int b);
};""", };""",
'') '')
# two argument constructor, across two lines, is okay # two argument constructor, across two lines, is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(int f, Foo(int f,
int b); int b);
};""", };""",
'') '')
# non-constructor (but similar name), is okay # non-constructor (but similar name), is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
aFoo(int f); aFoo(int f);
};""", };""",
'') '')
# constructor with void argument is okay # constructor with void argument is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(void); Foo(void);
};""", };""",
'') '')
# single argument method is okay # single argument method is okay
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Bar(int b); Bar(int b);
};""", };""",
'') '')
# comments should be ignored # comments should be ignored
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
// Foo(int f); // Foo(int f);
};""", };""",
'') '')
# single argument function following class definition is okay # single argument function following class definition is okay
# (okay, it's not actually valid, but we don't want a false positive) # (okay, it's not actually valid, but we don't want a false positive)
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(int f, int b); Foo(int f, int b);
}; };
Foo(int f);""", Foo(int f);""",
@ -988,18 +971,27 @@ class CpplintTest(CpplintTestBase):
'') '')
# single argument copy constructor is okay. # single argument copy constructor is okay.
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(const Foo&); Foo(const Foo&);
};""", };""",
'') '')
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo { """
class Foo {
Foo(Foo const&);
};""",
'')
self.TestMultiLineLint(
"""
class Foo {
Foo(Foo&); Foo(Foo&);
};""", };""",
'') '')
# templatized copy constructor is okay. # templatized copy constructor is okay.
self.TestMultiLineLint( self.TestMultiLineLint(
"""template<typename T> class Foo { """
template<typename T> class Foo {
Foo(const Foo<T>&); Foo(const Foo<T>&);
};""", };""",
'') '')
@ -1267,7 +1259,8 @@ class CpplintTest(CpplintTestBase):
'DISALLOW_COPY_AND_ASSIGN', 'DISALLOW_COPY_AND_ASSIGN',
'DISALLOW_IMPLICIT_CONSTRUCTORS'): 'DISALLOW_IMPLICIT_CONSTRUCTORS'):
self.TestMultiLineLint( self.TestMultiLineLint(
"""class A {' """
class A {'
public: public:
%s(A); %s(A);
};""" % macro_name, };""" % macro_name,
@ -1275,14 +1268,16 @@ class CpplintTest(CpplintTestBase):
' [readability/constructors] [3]') ' [readability/constructors] [3]')
self.TestMultiLineLint( self.TestMultiLineLint(
"""struct B {' """
struct B {'
%s(B); %s(B);
};""" % macro_name, };""" % macro_name,
('%s must be in the private: section' % macro_name) + ('%s must be in the private: section' % macro_name) +
' [readability/constructors] [3]') ' [readability/constructors] [3]')
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Outer1 {' """
class Outer1 {'
private: private:
struct Inner1 { struct Inner1 {
%s(Inner1); %s(Inner1);
@ -1293,7 +1288,8 @@ class CpplintTest(CpplintTestBase):
' [readability/constructors] [3]') ' [readability/constructors] [3]')
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Outer2 {' """
class Outer2 {'
private: private:
class Inner2 { class Inner2 {
%s(Inner2); %s(Inner2);
@ -1305,7 +1301,8 @@ class CpplintTest(CpplintTestBase):
# correctly. Use different macros for inner and outer classes so # correctly. Use different macros for inner and outer classes so
# that we can tell the error messages apart. # that we can tell the error messages apart.
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Outer3 { """
class Outer3 {
struct Inner3 { struct Inner3 {
DISALLOW_EVIL_CONSTRUCTORS(Inner3); DISALLOW_EVIL_CONSTRUCTORS(Inner3);
}; };
@ -1314,7 +1311,8 @@ class CpplintTest(CpplintTestBase):
('DISALLOW_EVIL_CONSTRUCTORS must be in the private: section' ('DISALLOW_EVIL_CONSTRUCTORS must be in the private: section'
' [readability/constructors] [3]')) ' [readability/constructors] [3]'))
self.TestMultiLineLint( self.TestMultiLineLint(
"""struct Outer4 { """
struct Outer4 {
class Inner4 { class Inner4 {
DISALLOW_EVIL_CONSTRUCTORS(Inner4); DISALLOW_EVIL_CONSTRUCTORS(Inner4);
}; };
@ -1527,12 +1525,16 @@ class CpplintTest(CpplintTestBase):
def testNonConstReference(self): def testNonConstReference(self):
# Passing a non-const reference as function parameter is forbidden. # Passing a non-const reference as function parameter is forbidden.
operand_error_message = ('Is this a non-const reference? ' operand_error_message = ('Is this a non-const reference? '
'If so, make const or use a pointer.' 'If so, make const or use a pointer: %s'
' [runtime/references] [2]') ' [runtime/references] [2]')
# Warn of use of a non-const reference in operators and functions # Warn of use of a non-const reference in operators and functions
self.TestLint('bool operator>(Foo& s, Foo& f);', operand_error_message) self.TestLint('bool operator>(Foo& s, Foo& f);',
self.TestLint('bool operator+(Foo& s, Foo& f);', operand_error_message) [operand_error_message % 'Foo& s',
self.TestLint('int len(Foo& s);', operand_error_message) operand_error_message % 'Foo& f'])
self.TestLint('bool operator+(Foo& s, Foo& f);',
[operand_error_message % 'Foo& s',
operand_error_message % 'Foo& f'])
self.TestLint('int len(Foo& s);', operand_error_message % 'Foo& s')
# Allow use of non-const references in a few specific cases # Allow use of non-const references in a few specific cases
self.TestLint('stream& operator>>(stream& s, Foo& f);', '') self.TestLint('stream& operator>>(stream& s, Foo& f);', '')
self.TestLint('stream& operator<<(stream& s, Foo& f);', '') self.TestLint('stream& operator<<(stream& s, Foo& f);', '')
@ -1543,6 +1545,28 @@ class CpplintTest(CpplintTestBase):
self.TestLint('void foo(const struct tm& tm);', '') self.TestLint('void foo(const struct tm& tm);', '')
# Passing a const reference to a typename is OK. # Passing a const reference to a typename is OK.
self.TestLint('void foo(const typename tm& tm);', '') self.TestLint('void foo(const typename tm& tm);', '')
# Const reference to a pointer type is OK.
self.TestLint('void foo(const Bar* const& p) {', '')
self.TestLint('void foo(Bar const* const& p) {', '')
self.TestLint('void foo(Bar* const& p) {', '')
# Const reference to a templated type is OK.
self.TestLint('void foo(const std::vector<std::string>& v);', '')
# Non-const reference to a pointer type is not OK.
self.TestLint('void foo(Bar*& p);',
operand_error_message % 'Bar*& p')
self.TestLint('void foo(const Bar*& p);',
operand_error_message % 'const Bar*& p')
self.TestLint('void foo(Bar const*& p);',
operand_error_message % 'Bar const*& p')
self.TestLint('void foo(struct Bar*& p);',
operand_error_message % 'struct Bar*& p')
self.TestLint('void foo(const struct Bar*& p);',
operand_error_message % 'const struct Bar*& p')
self.TestLint('void foo(struct Bar const*& p);',
operand_error_message % 'struct Bar const*& p')
# Non-const reference to a templated type is not OK.
self.TestLint('void foo(std::vector<int>& p);',
operand_error_message % 'std::vector<int>& p')
# Returning an address of something is not prohibited. # Returning an address of something is not prohibited.
self.TestLint('return &something;', '') self.TestLint('return &something;', '')
self.TestLint('if (condition) {return &something; }', '') self.TestLint('if (condition) {return &something; }', '')
@ -1556,6 +1580,34 @@ class CpplintTest(CpplintTestBase):
self.TestLint('for (const string& s : c)', '') self.TestLint('for (const string& s : c)', '')
self.TestLint('for (auto& r : c)', '') self.TestLint('for (auto& r : c)', '')
self.TestLint('for (typename Type& a : b)', '') self.TestLint('for (typename Type& a : b)', '')
# We don't get confused by some other uses of '&'.
self.TestLint('T& operator=(const T& t);', '')
self.TestLint('int g() { return (a & b); }', '')
self.TestLint('T& r = (T&)*(vp());', '')
self.TestLint('T& r = v', '')
self.TestLint('static_assert((kBits & kMask) == 0, "text");', '')
self.TestLint('COMPILE_ASSERT((kBits & kMask) == 0, text);', '')
# Another potential false positive. This one needs full parser
# state to reproduce as opposed to just TestLint.
error_collector = ErrorCollector(self.assert_)
cpplint.ProcessFileData(
'foo.cc', 'cc',
['// Copyright 2008 Your Company. All Rights Reserved.',
'void swap(int &x,',
' int &y) {',
'}',
'void swap(',
' sparsegroup<T, GROUP_SIZE, Alloc> &x,',
' sparsegroup<T, GROUP_SIZE, Alloc> &y) {',
'}',
'ostream& operator<<(',
' ostream& out',
' const dense_hash_set<Value, Hash, Equals, Alloc>& seq) {',
'}',
''],
error_collector)
self.assertEquals('', error_collector.Results())
def testBraceAtBeginOfLine(self): def testBraceAtBeginOfLine(self):
self.TestLint('{', self.TestLint('{',
@ -1628,6 +1680,7 @@ class CpplintTest(CpplintTestBase):
self.TestLint('((a+b))', '') self.TestLint('((a+b))', '')
self.TestLint('foo (foo)', 'Extra space before ( in function call' self.TestLint('foo (foo)', 'Extra space before ( in function call'
' [whitespace/parens] [4]') ' [whitespace/parens] [4]')
self.TestLint('} catch (const Foo& ex) {', '')
self.TestLint('typedef foo (*foo)(foo)', '') self.TestLint('typedef foo (*foo)(foo)', '')
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)', '')
@ -1652,6 +1705,15 @@ class CpplintTest(CpplintTestBase):
self.TestLint('for {', '') self.TestLint('for {', '')
self.TestLint('EXPECT_DEBUG_DEATH({', '') self.TestLint('EXPECT_DEBUG_DEATH({', '')
def testSemiColonAfterBraces(self):
self.TestLint('if (cond) {};',
'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 {};', '')
def testSpacingAroundElse(self): def testSpacingAroundElse(self):
self.TestLint('}else {', 'Missing space before else' self.TestLint('}else {', 'Missing space before else'
' [whitespace/braces] [5]') ' [whitespace/braces] [5]')
@ -2211,24 +2273,76 @@ class CpplintTest(CpplintTestBase):
self.TestLint(' char* one_space_indent = "public:";', self.TestLint(' char* one_space_indent = "public:";',
'Weird number of spaces at line-start. ' 'Weird number of spaces at line-start. '
'Are you using a 2-space indent? [whitespace/indent] [3]') 'Are you using a 2-space indent? [whitespace/indent] [3]')
self.TestLint(' public:', '')
self.TestLint(' public:', '')
self.TestLint(' public:', '')
def testLabel(self): def testSectionIndent(self):
self.TestLint('public:', self.TestMultiLineLint(
'Labels should always be indented at least one space. ' """
'If this is a member-initializer list in a constructor or ' class A {
'the base class list in a class definition, the colon should ' public: // no warning
'be on the following line. [whitespace/labels] [4]') private: // warning here
self.TestLint(' public:', '') };""",
self.TestLint(' public:', '') 'private: should be indented +1 space inside class A'
self.TestLint(' public:', '') ' [whitespace/indent] [3]')
self.TestLint(' public:', '') self.TestMultiLineLint(
self.TestLint(' public:', '') """
class B {
def testNotALabel(self): public: // no warning
self.TestLint('MyVeryLongNamespace::MyVeryLongClassName::', '') template<> struct C {
public: // warning here
protected: // no warning
};
};""",
'public: should be indented +1 space inside struct C'
' [whitespace/indent] [3]')
self.TestMultiLineLint(
"""
struct D {
};""",
'Closing brace should be aligned with beginning of struct D'
' [whitespace/indent] [3]')
self.TestMultiLineLint(
"""
template<typename E> class F {
};""",
'Closing brace should be aligned with beginning of class F'
' [whitespace/indent] [3]')
self.TestMultiLineLint(
"""
class G {
Q_OBJECT
public slots:
signals:
};""",
['public slots: should be indented +1 space inside class G'
' [whitespace/indent] [3]',
'signals: should be indented +1 space inside class G'
' [whitespace/indent] [3]'])
self.TestMultiLineLint(
"""
class H {
/* comments */ class I {
public: // no warning
private: // warning here
};
};""",
'private: should be indented +1 space inside class I'
' [whitespace/indent] [3]')
self.TestMultiLineLint(
"""
class J
: public ::K {
public: // no warning
protected: // warning here
};""",
'protected: should be indented +1 space inside class J'
' [whitespace/indent] [3]')
self.TestMultiLineLint(
"""
class L
: public M,
public ::N {
};""",
'')
def testTab(self): def testTab(self):
self.TestLint('\tint a;', self.TestLint('\tint a;',
@ -2368,7 +2482,8 @@ class CpplintTest(CpplintTestBase):
};""", };""",
'') '')
self.TestMultiLineLint( self.TestMultiLineLint(
"""class Foo """
class Foo
#ifdef DERIVE_FROM_GOO #ifdef DERIVE_FROM_GOO
: public Goo { : public Goo {
#else #else
@ -2966,7 +3081,13 @@ class OrderOfIncludesTest(CpplintTestBase):
def testRegression(self): def testRegression(self):
def Format(includes): def Format(includes):
return ''.join(['#include %s\n' % include for include in includes]) include_list = []
for header_path in includes:
if header_path:
include_list.append('#include %s\n' % header_path)
else:
include_list.append('\n')
return ''.join(include_list)
# Test singleton cases first. # Test singleton cases first.
self.TestLanguageRulesCheck('foo/foo.cc', Format(['"foo/foo.h"']), '') self.TestLanguageRulesCheck('foo/foo.cc', Format(['"foo/foo.h"']), '')
@ -2998,6 +3119,17 @@ class OrderOfIncludesTest(CpplintTestBase):
Format(['"foo/bar-inl.h"', Format(['"foo/bar-inl.h"',
'"foo/foo-inl.h"']), '"foo/foo-inl.h"']),
'') '')
self.TestLanguageRulesCheck(
'foo/foo.cc',
Format(['"foo/e.h"',
'"foo/b.h"', # warning here (e>b)
'"foo/c.h"',
'"foo/d.h"',
'"foo/a.h"']), # warning here (d>a)
['Include "foo/b.h" not in alphabetical order'
' [build/include_alpha] [4]',
'Include "foo/a.h" not in alphabetical order'
' [build/include_alpha] [4]'])
# -inl.h headers are no longer special. # -inl.h headers are no longer special.
self.TestLanguageRulesCheck('foo/foo.cc', self.TestLanguageRulesCheck('foo/foo.cc',
Format(['"foo/foo-inl.h"', '<string>']), Format(['"foo/foo-inl.h"', '<string>']),
@ -3030,6 +3162,21 @@ class OrderOfIncludesTest(CpplintTestBase):
'"base/google.h"', '"base/google.h"',
'"base/google-inl.h"']), '"base/google-inl.h"']),
'') '')
# Allow project includes to be separated by blank lines
self.TestLanguageRulesCheck('a/a.cc',
Format(['"a/a.h"',
'<string>',
'"base/google.h"',
'',
'"a/b.h"']),
'')
self.TestLanguageRulesCheck('a/a.cc',
Format(['"a/a.h"',
'<string>',
'"base/google.h"',
'"a/b.h"']),
'Include "a/b.h" not in alphabetical '
'order [build/include_alpha] [4]')
class CheckForFunctionLengthsTest(CpplintTestBase): class CheckForFunctionLengthsTest(CpplintTestBase):
@ -3386,6 +3533,7 @@ class NestnigStateTest(unittest.TestCase):
self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo)) self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo))
self.assertEquals(self.nesting_state.stack[0].name, 'A') self.assertEquals(self.nesting_state.stack[0].name, 'A')
self.assertFalse(self.nesting_state.stack[0].is_derived) self.assertFalse(self.nesting_state.stack[0].is_derived)
self.assertEquals(self.nesting_state.stack[0].class_indent, 0)
self.UpdateWithLines(['};', self.UpdateWithLines(['};',
'struct B : public A {']) 'struct B : public A {'])
@ -3406,7 +3554,7 @@ class NestnigStateTest(unittest.TestCase):
'template<T>']) 'template<T>'])
self.assertEquals(len(self.nesting_state.stack), 0) self.assertEquals(len(self.nesting_state.stack), 0)
self.UpdateWithLines(['class D {', 'class E {']) self.UpdateWithLines(['class D {', ' class E {'])
self.assertEquals(len(self.nesting_state.stack), 2) self.assertEquals(len(self.nesting_state.stack), 2)
self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo)) self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo))
self.assertEquals(self.nesting_state.stack[0].name, 'D') self.assertEquals(self.nesting_state.stack[0].name, 'D')
@ -3414,6 +3562,7 @@ class NestnigStateTest(unittest.TestCase):
self.assertTrue(isinstance(self.nesting_state.stack[1], cpplint._ClassInfo)) self.assertTrue(isinstance(self.nesting_state.stack[1], cpplint._ClassInfo))
self.assertEquals(self.nesting_state.stack[1].name, 'E') self.assertEquals(self.nesting_state.stack[1].name, 'E')
self.assertFalse(self.nesting_state.stack[1].is_derived) self.assertFalse(self.nesting_state.stack[1].is_derived)
self.assertEquals(self.nesting_state.stack[1].class_indent, 2)
self.assertEquals(self.nesting_state.InnermostClass().name, 'E') self.assertEquals(self.nesting_state.InnermostClass().name, 'E')
self.UpdateWithLines(['}', '}']) self.UpdateWithLines(['}', '}'])
@ -3553,6 +3702,18 @@ class NestnigStateTest(unittest.TestCase):
self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo)) self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo))
self.assertEquals(self.nesting_state.stack[0].name, 'D') self.assertEquals(self.nesting_state.stack[0].name, 'D')
def testTemplateInnerClass(self):
self.UpdateWithLines(['class A {',
' public:'])
self.assertEquals(len(self.nesting_state.stack), 1)
self.assertTrue(isinstance(self.nesting_state.stack[0], cpplint._ClassInfo))
self.UpdateWithLines([' template <class B>',
' class C<alloc<B> >',
' : public A {'])
self.assertEquals(len(self.nesting_state.stack), 2)
self.assertTrue(isinstance(self.nesting_state.stack[1], cpplint._ClassInfo))
def testArguments(self): def testArguments(self):
self.UpdateWithLines(['class A {']) self.UpdateWithLines(['class A {'])
self.assertEquals(len(self.nesting_state.stack), 1) self.assertEquals(len(self.nesting_state.stack), 1)