Commit 35589e67 authored by erg@google.com's avatar erg@google.com

Update cpplint.py to r62.

Review URL: http://codereview.chromium.org/5186001

git-svn-id: svn://svn.chromium.org/chrome/trunk/tools/depot_tools@66483 0039d316-1c4b-4281-b951-d872f2087c98
parent 3bb0d6fa
...@@ -102,8 +102,9 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] ...@@ -102,8 +102,9 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
certain of the problem, and 1 meaning it could be a legitimate construct. certain of the problem, and 1 meaning it could be a legitimate construct.
This will miss some errors, and is not a substitute for a code review. This will miss some errors, and is not a substitute for a code review.
To prevent specific lines from being linted, add a '// NOLINT' comment to the To suppress false-positive errors of a certain category, add a
end of the line. 'NOLINT(category)' comment to the line. NOLINT or NOLINT(*)
suppresses errors of all categories on that line.
The files passed in will be linted; at least one file must be provided. The files passed in will be linted; at least one file must be provided.
Linted extensions are .cc, .cpp, and .h. Other file types will be ignored. Linted extensions are .cc, .cpp, and .h. Other file types will be ignored.
...@@ -145,64 +146,65 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] ...@@ -145,64 +146,65 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
# If you add a new error message with a new category, add it to the list # If you add a new error message with a new category, add it to the list
# here! cpplint_unittest.py should tell you if you forget to do this. # here! cpplint_unittest.py should tell you if you forget to do this.
# \ used for clearer layout -- pylint: disable-msg=C6013 # \ used for clearer layout -- pylint: disable-msg=C6013
_ERROR_CATEGORIES = '''\ _ERROR_CATEGORIES = [
build/class 'build/class',
build/deprecated 'build/deprecated',
build/endif_comment 'build/endif_comment',
build/forward_decl 'build/forward_decl',
build/header_guard 'build/header_guard',
build/include 'build/include',
build/include_alpha 'build/include_alpha',
build/include_order 'build/include_order',
build/include_what_you_use 'build/include_what_you_use',
build/namespaces 'build/namespaces',
build/printf_format 'build/printf_format',
build/storage_class 'build/storage_class',
legal/copyright 'legal/copyright',
readability/braces 'readability/braces',
readability/casting 'readability/casting',
readability/check 'readability/check',
readability/constructors 'readability/constructors',
readability/fn_size 'readability/fn_size',
readability/function 'readability/function',
readability/multiline_comment 'readability/multiline_comment',
readability/multiline_string 'readability/multiline_string',
readability/streams 'readability/nolint',
readability/todo 'readability/streams',
readability/utf8 'readability/todo',
runtime/arrays 'readability/utf8',
runtime/casting 'runtime/arrays',
runtime/explicit 'runtime/casting',
runtime/int 'runtime/explicit',
runtime/init 'runtime/int',
runtime/invalid_increment 'runtime/init',
runtime/member_string_references 'runtime/invalid_increment',
runtime/memset 'runtime/member_string_references',
runtime/operator 'runtime/memset',
runtime/printf 'runtime/operator',
runtime/printf_format 'runtime/printf',
runtime/references 'runtime/printf_format',
runtime/rtti 'runtime/references',
runtime/sizeof 'runtime/rtti',
runtime/string 'runtime/sizeof',
runtime/threadsafe_fn 'runtime/string',
runtime/virtual 'runtime/threadsafe_fn',
whitespace/blank_line 'runtime/virtual',
whitespace/braces 'whitespace/blank_line',
whitespace/comma 'whitespace/braces',
whitespace/comments 'whitespace/comma',
whitespace/end_of_line 'whitespace/comments',
whitespace/ending_newline 'whitespace/end_of_line',
whitespace/indent 'whitespace/ending_newline',
whitespace/labels 'whitespace/indent',
whitespace/line_length 'whitespace/labels',
whitespace/newline 'whitespace/line_length',
whitespace/operators 'whitespace/newline',
whitespace/parens 'whitespace/operators',
whitespace/semicolon 'whitespace/parens',
whitespace/tab 'whitespace/semicolon',
whitespace/todo 'whitespace/tab',
''' 'whitespace/todo'
]
# The default state of the category filter. This is overrided by the --filter= # The default state of the category filter. This is overrided by the --filter=
# flag. By default all errors are on, so only add here categories that should be # flag. By default all errors are on, so only add here categories that should be
...@@ -218,8 +220,8 @@ _DEFAULT_FILTERS = [ '-build/include_alpha' ] ...@@ -218,8 +220,8 @@ _DEFAULT_FILTERS = [ '-build/include_alpha' ]
_STL_HEADERS = frozenset([ _STL_HEADERS = frozenset([
'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception', 'algobase.h', 'algorithm', 'alloc.h', 'bitset', 'deque', 'exception',
'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set', 'function.h', 'functional', 'hash_map', 'hash_map.h', 'hash_set',
'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'pair.h', 'hash_set.h', 'iterator', 'list', 'list.h', 'map', 'memory', 'new',
'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack', 'pair.h', 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack',
'stl_alloc.h', 'stl_relops.h', 'type_traits.h', 'stl_alloc.h', 'stl_relops.h', 'type_traits.h',
'utility', 'vector', 'vector.h', 'utility', 'vector', 'vector.h',
]) ])
...@@ -287,6 +289,61 @@ _OTHER_HEADER = 5 ...@@ -287,6 +289,61 @@ _OTHER_HEADER = 5
_regexp_compile_cache = {} _regexp_compile_cache = {}
# Finds occurrences of NOLINT or NOLINT(...).
_RE_SUPPRESSION = re.compile(r'\bNOLINT\b(\([^)]*\))?')
# {str, set(int)}: a map from error categories to sets of linenumbers
# on which those errors are expected and should be suppressed.
_error_suppressions = {}
def ParseNolintSuppressions(filename, raw_line, linenum, error):
"""Updates the global list of error-suppressions.
Parses any NOLINT comments on the current line, updating the global
error_suppressions store. Reports an error if the NOLINT comment
was malformed.
Args:
filename: str, the name of the input file.
raw_line: str, the line of input text, with comments.
linenum: int, the number of the current line.
error: function, an error handler.
"""
# FIXME(adonovan): "NOLINT(" is misparsed as NOLINT(*).
m = _RE_SUPPRESSION.search(raw_line)
if m:
category = m.group(1)
if category in (None, '(*)'): # => "suppress all"
_error_suppressions.setdefault(None, set()).add(linenum)
else:
if category.startswith('(') and category.endswith(')'):
category = category[1:-1]
if category in _ERROR_CATEGORIES:
_error_suppressions.setdefault(category, set()).add(linenum)
else:
error(filename, linenum, 'readability/nolint', 5,
'Unknown NOLINT error category: %s' % category)
def ResetNolintSuppressions():
"Resets the set of NOLINT suppressions to empty."
_error_suppressions.clear()
def IsErrorSuppressedByNolint(category, linenum):
"""Returns true if the specified error category is suppressed on this line.
Consults the global error_suppressions map populated by
ParseNolintSuppressions/ResetNolintSuppressions.
Args:
category: str, the category of the error.
linenum: int, the current line number.
Returns:
bool, True iff the error should be suppressed due to a NOLINT comment.
"""
return (linenum in _error_suppressions.get(category, set()) or
linenum in _error_suppressions.get(None, set()))
def Match(pattern, s): def Match(pattern, s):
"""Matches the string with the pattern, caching the compiled regexp.""" """Matches the string with the pattern, caching the compiled regexp."""
...@@ -655,15 +712,18 @@ class FileInfo: ...@@ -655,15 +712,18 @@ class FileInfo:
prefix = os.path.commonprefix([root_dir, project_dir]) prefix = os.path.commonprefix([root_dir, project_dir])
return fullname[len(prefix) + 1:] return fullname[len(prefix) + 1:]
# Not SVN? Try to find a git top level directory by searching up from the # Not SVN? Try to find a git or hg top level directory by searching up
# current path. # from the current path.
root_dir = os.path.dirname(fullname) root_dir = os.path.dirname(fullname)
while (root_dir != os.path.dirname(root_dir) and while (root_dir != os.path.dirname(root_dir) and
not os.path.exists(os.path.join(root_dir, ".git"))): not os.path.exists(os.path.join(root_dir, ".git")) and
not os.path.exists(os.path.join(root_dir, ".hg"))):
root_dir = os.path.dirname(root_dir) root_dir = os.path.dirname(root_dir)
if os.path.exists(os.path.join(root_dir, ".git")):
prefix = os.path.commonprefix([root_dir, project_dir]) if (os.path.exists(os.path.join(root_dir, ".git")) or
return fullname[len(prefix) + 1:] os.path.exists(os.path.join(root_dir, ".hg"))):
prefix = os.path.commonprefix([root_dir, project_dir])
return fullname[len(prefix) + 1:]
# Don't know what to do; header guard warnings may be wrong... # Don't know what to do; header guard warnings may be wrong...
return fullname return fullname
...@@ -699,10 +759,15 @@ class FileInfo: ...@@ -699,10 +759,15 @@ class FileInfo:
return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx') return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
def _ShouldPrintError(category, confidence): def _ShouldPrintError(category, confidence, linenum):
"""Returns true iff confidence >= verbose, and category passes filter.""" """Returns true iff confidence >= verbose, category passes
# There are two ways we might decide not to print an error message: filter and is not NOLINT-suppressed."""
# There are three ways we might decide not to print an error message:
# a "NOLINT(category)" comment appears in the source,
# the verbosity level isn't high enough, or the filters filter it out. # the verbosity level isn't high enough, or the filters filter it out.
if IsErrorSuppressedByNolint(category, linenum):
return False
if confidence < _cpplint_state.verbose_level: if confidence < _cpplint_state.verbose_level:
return False return False
...@@ -729,6 +794,10 @@ def Error(filename, linenum, category, confidence, message): ...@@ -729,6 +794,10 @@ def Error(filename, linenum, category, confidence, message):
that is, how certain we are this is a legitimate style regression, and that is, how certain we are this is a legitimate style regression, and
not a misidentification or a use that's sometimes justified. not a misidentification or a use that's sometimes justified.
False positives can be suppressed by the use of
"cpplint(category)" comments on the offending line. These are
parsed into _error_suppressions.
Args: Args:
filename: The name of the file containing the error. filename: The name of the file containing the error.
linenum: The number of the line containing the error. linenum: The number of the line containing the error.
...@@ -740,9 +809,7 @@ def Error(filename, linenum, category, confidence, message): ...@@ -740,9 +809,7 @@ def Error(filename, linenum, category, confidence, message):
and 1 meaning that it could be a legitimate construct. and 1 meaning that it could be a legitimate construct.
message: The error message. message: The error message.
""" """
# There are two ways we might decide not to print an error message: if _ShouldPrintError(category, confidence, linenum):
# the verbosity level isn't high enough, or the filters filter it out.
if _ShouldPrintError(category, confidence):
_cpplint_state.IncrementErrorCount(category) _cpplint_state.IncrementErrorCount(category)
if _cpplint_state.output_format == 'vs7': if _cpplint_state.output_format == 'vs7':
sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
...@@ -960,6 +1027,10 @@ def GetHeaderGuardCPPVariable(filename): ...@@ -960,6 +1027,10 @@ def GetHeaderGuardCPPVariable(filename):
""" """
# Restores original filename in case that cpplint is invoked from Emacs's
# flymake.
filename = re.sub(r'_flymake\.h$', '.h', filename)
fileinfo = FileInfo(filename) fileinfo = FileInfo(filename)
return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_' return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_'
...@@ -1006,20 +1077,23 @@ def CheckForHeaderGuard(filename, lines, error): ...@@ -1006,20 +1077,23 @@ def CheckForHeaderGuard(filename, lines, error):
# The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
# for backward compatibility. # for backward compatibility.
if ifndef != cppvar and not Search(r'\bNOLINT\b', lines[ifndef_linenum]): if ifndef != cppvar:
error_level = 0 error_level = 0
if ifndef != cppvar + '_': if ifndef != cppvar + '_':
error_level = 5 error_level = 5
ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
error)
error(filename, ifndef_linenum, 'build/header_guard', error_level, error(filename, ifndef_linenum, 'build/header_guard', error_level,
'#ifndef header guard has wrong style, please use: %s' % cppvar) '#ifndef header guard has wrong style, please use: %s' % cppvar)
if (endif != ('#endif // %s' % cppvar) and if endif != ('#endif // %s' % cppvar):
not Search(r'\bNOLINT\b', lines[endif_linenum])):
error_level = 0 error_level = 0
if endif != ('#endif // %s' % (cppvar + '_')): if endif != ('#endif // %s' % (cppvar + '_')):
error_level = 5 error_level = 5
ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
error)
error(filename, endif_linenum, 'build/header_guard', error_level, error(filename, endif_linenum, 'build/header_guard', error_level,
'#endif line should be "#endif // %s"' % cppvar) '#endif line should be "#endif // %s"' % cppvar)
...@@ -1517,8 +1591,7 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, ...@@ -1517,8 +1591,7 @@ def CheckForFunctionLengths(filename, clean_lines, linenum,
error(filename, linenum, 'readability/fn_size', 5, error(filename, linenum, 'readability/fn_size', 5,
'Lint failed to find start of function body.') 'Lint failed to find start of function body.')
elif Match(r'^\}\s*$', line): # function end elif Match(r'^\}\s*$', line): # function end
if not Search(r'\bNOLINT\b', raw_line): function_state.Check(error, filename, linenum)
function_state.Check(error, filename, linenum)
function_state.End() function_state.End()
elif not Match(r'^\s*$', line): elif not Match(r'^\s*$', line):
function_state.Count() # Count non-blank/non-comment lines. function_state.Count() # Count non-blank/non-comment lines.
...@@ -1663,9 +1736,12 @@ def CheckSpacing(filename, clean_lines, linenum, error): ...@@ -1663,9 +1736,12 @@ def CheckSpacing(filename, clean_lines, linenum, error):
# but some lines are exceptions -- e.g. if they're big # but some lines are exceptions -- e.g. if they're big
# comment delimiters like: # comment delimiters like:
# //---------------------------------------------------------- # //----------------------------------------------------------
# or are an empty C++ style Doxygen comment, like:
# ///
# or they begin with multiple slashes followed by a space: # or they begin with multiple slashes followed by a space:
# //////// Header comment # //////// Header comment
match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or
Search(r'^/$', line[commentend:]) or
Search(r'^/+ ', line[commentend:])) Search(r'^/+ ', line[commentend:]))
if not match: if not match:
error(filename, linenum, 'whitespace/comments', 4, error(filename, linenum, 'whitespace/comments', 4,
...@@ -2025,8 +2101,10 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error): ...@@ -2025,8 +2101,10 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, error):
line): line):
error(filename, linenum, 'whitespace/labels', 4, error(filename, linenum, 'whitespace/labels', 4,
'Labels should always be indented at least one space. ' 'Labels should always be indented at least one space. '
'If this is a member-initializer list in a constructor, ' 'If this is a member-initializer list in a constructor or '
'the colon should be on the line after the definition header.') '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
...@@ -2399,7 +2477,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state, ...@@ -2399,7 +2477,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, include_state,
# When snprintf is used, the second argument shouldn't be a literal. # When snprintf is used, the second argument shouldn't be a literal.
match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line) match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
if match: if match and match.group(2) != '0':
# If 2nd arg is zero, snprintf is used to calculate size.
error(filename, linenum, 'runtime/printf', 3, error(filename, linenum, 'runtime/printf', 3,
'If you can, use sizeof(%s) instead of %s as the 2nd arg ' 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
'to snprintf.' % (match.group(1), match.group(2))) 'to snprintf.' % (match.group(1), match.group(2)))
...@@ -2748,8 +2827,13 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, ...@@ -2748,8 +2827,13 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
continue continue
# String is special -- it is a non-templatized type in STL. # String is special -- it is a non-templatized type in STL.
if _RE_PATTERN_STRING.search(line): m = _RE_PATTERN_STRING.search(line)
required['<string>'] = (linenum, 'string') if m:
# Don't warn about strings in non-STL namespaces:
# (We check only the first match per line; good enough.)
prefix = line[:m.start()]
if prefix.endswith('std::') or not prefix.endswith('::'):
required['<string>'] = (linenum, 'string')
for pattern, template, header in _re_pattern_algorithm_header: for pattern, template, header in _re_pattern_algorithm_header:
if pattern.search(line): if pattern.search(line):
...@@ -2781,9 +2865,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, ...@@ -2781,9 +2865,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error,
# found. # found.
# e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h' # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
# instead of 'foo_flymake.h' # instead of 'foo_flymake.h'
emacs_flymake_suffix = '_flymake.cc' abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
if abs_filename.endswith(emacs_flymake_suffix):
abs_filename = abs_filename[:-len(emacs_flymake_suffix)] + '.cc'
# include_state is modified during iteration, so we iterate over a copy of # include_state is modified during iteration, so we iterate over a copy of
# the keys. # the keys.
...@@ -2834,9 +2916,8 @@ def ProcessLine(filename, file_extension, ...@@ -2834,9 +2916,8 @@ def ProcessLine(filename, file_extension,
""" """
raw_lines = clean_lines.raw_lines raw_lines = clean_lines.raw_lines
ParseNolintSuppressions(filename, raw_lines[line], line, error)
CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
if Search(r'\bNOLINT\b', raw_lines[line]): # ignore nolint lines
return
CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
CheckStyle(filename, clean_lines, line, file_extension, error) CheckStyle(filename, clean_lines, line, file_extension, error)
CheckLanguage(filename, clean_lines, line, file_extension, include_state, CheckLanguage(filename, clean_lines, line, file_extension, include_state,
...@@ -2864,6 +2945,8 @@ def ProcessFileData(filename, file_extension, lines, error): ...@@ -2864,6 +2945,8 @@ def ProcessFileData(filename, file_extension, lines, error):
function_state = _FunctionState() function_state = _FunctionState()
class_state = _ClassState() class_state = _ClassState()
ResetNolintSuppressions()
CheckForCopyright(filename, lines, error) CheckForCopyright(filename, lines, error)
if file_extension == 'h': if file_extension == 'h':
...@@ -2884,7 +2967,6 @@ def ProcessFileData(filename, file_extension, lines, error): ...@@ -2884,7 +2967,6 @@ def ProcessFileData(filename, file_extension, lines, error):
CheckForNewlineAtEOF(filename, lines, error) CheckForNewlineAtEOF(filename, lines, error)
def ProcessFile(filename, vlevel): def ProcessFile(filename, vlevel):
"""Does google-lint on a single file. """Does google-lint on a single file.
...@@ -2966,7 +3048,7 @@ def PrintCategories(): ...@@ -2966,7 +3048,7 @@ def PrintCategories():
These are the categories used to filter messages via --filter. These are the categories used to filter messages via --filter.
""" """
sys.stderr.write(_ERROR_CATEGORIES) sys.stderr.write(''.join(' %s\n' % cat for cat in _ERROR_CATEGORIES))
sys.exit(0) sys.exit(0)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment