| Index: cpplint/cpplint.py
|
| ===================================================================
|
| --- cpplint/cpplint.py (revision 43)
|
| +++ cpplint/cpplint.py (working copy)
|
| @@ -102,8 +102,9 @@
|
| 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.
|
|
|
| - To prevent specific lines from being linted, add a '// NOLINT' comment to the
|
| - end of the line.
|
| + To suppress false-positive errors of a certain category, add a
|
| + '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.
|
| Linted extensions are .cc, .cpp, and .h. Other file types will be ignored.
|
| @@ -145,64 +146,65 @@
|
| # 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
|
| - build/endif_comment
|
| - build/forward_decl
|
| - build/header_guard
|
| - build/include
|
| - build/include_alpha
|
| - build/include_order
|
| - build/include_what_you_use
|
| - build/namespaces
|
| - build/printf_format
|
| - build/storage_class
|
| - legal/copyright
|
| - readability/braces
|
| - readability/casting
|
| - readability/check
|
| - readability/constructors
|
| - readability/fn_size
|
| - readability/function
|
| - readability/multiline_comment
|
| - readability/multiline_string
|
| - readability/streams
|
| - readability/todo
|
| - readability/utf8
|
| - runtime/arrays
|
| - runtime/casting
|
| - runtime/explicit
|
| - runtime/int
|
| - runtime/init
|
| - runtime/invalid_increment
|
| - runtime/member_string_references
|
| - runtime/memset
|
| - runtime/operator
|
| - runtime/printf
|
| - runtime/printf_format
|
| - runtime/references
|
| - runtime/rtti
|
| - runtime/sizeof
|
| - runtime/string
|
| - runtime/threadsafe_fn
|
| - runtime/virtual
|
| - whitespace/blank_line
|
| - whitespace/braces
|
| - whitespace/comma
|
| - whitespace/comments
|
| - whitespace/end_of_line
|
| - whitespace/ending_newline
|
| - whitespace/indent
|
| - whitespace/labels
|
| - whitespace/line_length
|
| - whitespace/newline
|
| - whitespace/operators
|
| - whitespace/parens
|
| - whitespace/semicolon
|
| - whitespace/tab
|
| - whitespace/todo
|
| -'''
|
| +_ERROR_CATEGORIES = [
|
| + 'build/class',
|
| + 'build/deprecated',
|
| + 'build/endif_comment',
|
| + 'build/forward_decl',
|
| + 'build/header_guard',
|
| + 'build/include',
|
| + 'build/include_alpha',
|
| + 'build/include_order',
|
| + 'build/include_what_you_use',
|
| + 'build/namespaces',
|
| + 'build/printf_format',
|
| + 'build/storage_class',
|
| + 'legal/copyright',
|
| + 'readability/braces',
|
| + 'readability/casting',
|
| + 'readability/check',
|
| + 'readability/constructors',
|
| + 'readability/fn_size',
|
| + 'readability/function',
|
| + 'readability/multiline_comment',
|
| + 'readability/multiline_string',
|
| + 'readability/nolint',
|
| + 'readability/streams',
|
| + 'readability/todo',
|
| + 'readability/utf8',
|
| + 'runtime/arrays',
|
| + 'runtime/casting',
|
| + 'runtime/explicit',
|
| + 'runtime/int',
|
| + 'runtime/init',
|
| + 'runtime/invalid_increment',
|
| + 'runtime/member_string_references',
|
| + 'runtime/memset',
|
| + 'runtime/operator',
|
| + 'runtime/printf',
|
| + 'runtime/printf_format',
|
| + 'runtime/references',
|
| + 'runtime/rtti',
|
| + 'runtime/sizeof',
|
| + 'runtime/string',
|
| + 'runtime/threadsafe_fn',
|
| + 'runtime/virtual',
|
| + 'whitespace/blank_line',
|
| + 'whitespace/braces',
|
| + 'whitespace/comma',
|
| + 'whitespace/comments',
|
| + 'whitespace/end_of_line',
|
| + 'whitespace/ending_newline',
|
| + 'whitespace/indent',
|
| + 'whitespace/labels',
|
| + 'whitespace/line_length',
|
| + 'whitespace/newline',
|
| + 'whitespace/operators',
|
| + 'whitespace/parens',
|
| + 'whitespace/semicolon',
|
| + 'whitespace/tab',
|
| + 'whitespace/todo'
|
| + ]
|
|
|
| # 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
|
| @@ -218,8 +220,8 @@
|
| _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', 'pair.h',
|
| - 'pthread_alloc', 'queue', 'set', 'set.h', 'sstream', 'stack',
|
| + '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',
|
| ])
|
| @@ -287,7 +289,62 @@
|
|
|
| _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):
|
| """Matches the string with the pattern, caching the compiled regexp."""
|
| # The regexp compilation caching is inlined in both Match and Search for
|
| @@ -701,10 +758,15 @@
|
| return self.Extension()[1:] in ('c', 'cc', 'cpp', 'cxx')
|
|
|
|
|
| -def _ShouldPrintError(category, confidence):
|
| - """Returns true iff confidence >= verbose, and category passes filter."""
|
| - # There are two ways we might decide not to print an error message:
|
| +def _ShouldPrintError(category, confidence, linenum):
|
| + """Returns true iff confidence >= verbose, category passes
|
| + 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.
|
| + if IsErrorSuppressedByNolint(category, linenum):
|
| + return False
|
| if confidence < _cpplint_state.verbose_level:
|
| return False
|
|
|
| @@ -731,6 +793,10 @@
|
| that is, how certain we are this is a legitimate style regression, and
|
| 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:
|
| filename: The name of the file containing the error.
|
| linenum: The number of the line containing the error.
|
| @@ -742,9 +808,7 @@
|
| and 1 meaning that it could be a legitimate construct.
|
| message: The error message.
|
| """
|
| - # There are two ways we might decide not to print an error message:
|
| - # the verbosity level isn't high enough, or the filters filter it out.
|
| - if _ShouldPrintError(category, confidence):
|
| + if _ShouldPrintError(category, confidence, linenum):
|
| _cpplint_state.IncrementErrorCount(category)
|
| if _cpplint_state.output_format == 'vs7':
|
| sys.stderr.write('%s(%s): %s [%s] [%d]\n' % (
|
| @@ -962,6 +1026,10 @@
|
|
|
| """
|
|
|
| + # Restores original filename in case that cpplint is invoked from Emacs's
|
| + # flymake.
|
| + filename = re.sub(r'_flymake\.h$', '.h', filename)
|
| +
|
| fileinfo = FileInfo(filename)
|
| return re.sub(r'[-./\s]', '_', fileinfo.RepositoryName()).upper() + '_'
|
|
|
| @@ -1008,20 +1076,23 @@
|
|
|
| # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__
|
| # for backward compatibility.
|
| - if ifndef != cppvar and not Search(r'\bNOLINT\b', lines[ifndef_linenum]):
|
| + if ifndef != cppvar:
|
| error_level = 0
|
| if ifndef != cppvar + '_':
|
| error_level = 5
|
|
|
| + ParseNolintSuppressions(filename, lines[ifndef_linenum], ifndef_linenum,
|
| + error)
|
| error(filename, ifndef_linenum, 'build/header_guard', error_level,
|
| '#ifndef header guard has wrong style, please use: %s' % cppvar)
|
|
|
| - if (endif != ('#endif // %s' % cppvar) and
|
| - not Search(r'\bNOLINT\b', lines[endif_linenum])):
|
| + if endif != ('#endif // %s' % cppvar):
|
| error_level = 0
|
| if endif != ('#endif // %s' % (cppvar + '_')):
|
| error_level = 5
|
|
|
| + ParseNolintSuppressions(filename, lines[endif_linenum], endif_linenum,
|
| + error)
|
| error(filename, endif_linenum, 'build/header_guard', error_level,
|
| '#endif line should be "#endif // %s"' % cppvar)
|
|
|
| @@ -1519,8 +1590,7 @@
|
| error(filename, linenum, 'readability/fn_size', 5,
|
| 'Lint failed to find start of function body.')
|
| 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()
|
| elif not Match(r'^\s*$', line):
|
| function_state.Count() # Count non-blank/non-comment lines.
|
| @@ -2027,9 +2097,11 @@
|
| 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, '
|
| - 'the colon should be on the line after the definition header.')
|
| + '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.
|
| is_header_guard = False
|
| if file_extension == 'h':
|
| @@ -2401,7 +2473,8 @@
|
|
|
| # When snprintf is used, the second argument shouldn't be a literal.
|
| 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,
|
| 'If you can, use sizeof(%s) instead of %s as the 2nd arg '
|
| 'to snprintf.' % (match.group(1), match.group(2)))
|
| @@ -2750,8 +2823,13 @@
|
| continue
|
|
|
| # String is special -- it is a non-templatized type in STL.
|
| - if _RE_PATTERN_STRING.search(line):
|
| - required['<string>'] = (linenum, 'string')
|
| + m = _RE_PATTERN_STRING.search(line)
|
| + 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:
|
| if pattern.search(line):
|
| @@ -2783,9 +2861,7 @@
|
| # found.
|
| # e.g. If the file name is 'foo_flymake.cc', we should search for 'foo.h'
|
| # instead of 'foo_flymake.h'
|
| - emacs_flymake_suffix = '_flymake.cc'
|
| - if abs_filename.endswith(emacs_flymake_suffix):
|
| - abs_filename = abs_filename[:-len(emacs_flymake_suffix)] + '.cc'
|
| + abs_filename = re.sub(r'_flymake\.cc$', '.cc', abs_filename)
|
|
|
| # include_state is modified during iteration, so we iterate over a copy of
|
| # the keys.
|
| @@ -2836,9 +2912,8 @@
|
|
|
| """
|
| raw_lines = clean_lines.raw_lines
|
| + ParseNolintSuppressions(filename, raw_lines[line], line, 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)
|
| CheckStyle(filename, clean_lines, line, file_extension, error)
|
| CheckLanguage(filename, clean_lines, line, file_extension, include_state,
|
| @@ -2866,6 +2941,8 @@
|
| function_state = _FunctionState()
|
| class_state = _ClassState()
|
|
|
| + ResetNolintSuppressions()
|
| +
|
| CheckForCopyright(filename, lines, error)
|
|
|
| if file_extension == 'h':
|
| @@ -2886,7 +2963,6 @@
|
|
|
| CheckForNewlineAtEOF(filename, lines, error)
|
|
|
| -
|
| def ProcessFile(filename, vlevel):
|
| """Does google-lint on a single file.
|
|
|
| @@ -2968,7 +3044,7 @@
|
|
|
| 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)
|
|
|
|
|
|
|