cpplint: Pull in upstream changes

The changes include:
- root flag now can process non-subdirectories
-- e7ddd2af62

- root flag can be configured with CPPLINT.cfg
-- 2322e4ffaa

- root setting is relative to the CPPLINT.cfg file
-- 8a87a46cc7

- Cleans up header file detection for hpp and hxx

- Adds quite mode

Bug: 852898

Change-Id: Id44bbfadc913cc27192049ab9a222f22d0deab5d
Reviewed-on: https://chromium-review.googlesource.com/1108381
Commit-Queue: Amin Hassani <ahassani@chromium.org>
Reviewed-by: Aaron Gable <agable@chromium.org>
changes/81/1108381/6
Amin Hassani 7 years ago committed by Commit Bot
parent 124365b8ea
commit f7e1e10db5

230
cpplint.py vendored

@ -53,12 +53,19 @@ import sre_compile
import string import string
import sys import sys
import unicodedata import unicodedata
import sysconfig
try:
xrange # Python 2
except NameError:
xrange = range # Python 3
_USAGE = """ _USAGE = """
Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
[--counting=total|toplevel|detailed] [--root=subdir] [--counting=total|toplevel|detailed] [--root=subdir]
[--linelength=digits] [--linelength=digits] [--headers=x,y,...]
[--quiet]
<file> [file] ... <file> [file] ...
The style guidelines this tries to follow are those in The style guidelines this tries to follow are those in
@ -85,6 +92,9 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
verbose=# verbose=#
Specify a number 0-5 to restrict errors to certain verbosity levels. Specify a number 0-5 to restrict errors to certain verbosity levels.
quiet
Don't print anything if no errors are found.
filter=-x,+y,... filter=-x,+y,...
Specify a comma-separated list of category-filters to apply: only Specify a comma-separated list of category-filters to apply: only
error messages whose category names pass the filters will be printed. error messages whose category names pass the filters will be printed.
@ -116,12 +126,13 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
ignored. ignored.
Examples: Examples:
Assuming that src/.git exists, the header guard CPP variables for Assuming that top/src/.git exists (and cwd=top/src), the header guard
src/chrome/browser/ui/browser.h are: CPP variables for top/src/chrome/browser/ui/browser.h are:
No flag => CHROME_BROWSER_UI_BROWSER_H_ No flag => CHROME_BROWSER_UI_BROWSER_H_
--root=chrome => BROWSER_UI_BROWSER_H_ --root=chrome => BROWSER_UI_BROWSER_H_
--root=chrome/browser => UI_BROWSER_H_ --root=chrome/browser => UI_BROWSER_H_
--root=.. => SRC_CHROME_BROWSER_UI_BROWSER_H_
linelength=digits linelength=digits
This is the allowed line length for the project. The default value is This is the allowed line length for the project. The default value is
@ -136,6 +147,14 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
Examples: Examples:
--extensions=hpp,cpp --extensions=hpp,cpp
headers=x,y,...
The header extensions that cpplint will treat as .h in checks. Values are
automatically added to --extensions list.
Examples:
--headers=hpp,hxx
--headers=hpp
cpplint.py supports per-directory configurations specified in CPPLINT.cfg cpplint.py supports per-directory configurations specified in CPPLINT.cfg
files. CPPLINT.cfg file can contain a number of key=value pairs. files. CPPLINT.cfg file can contain a number of key=value pairs.
Currently the following options are supported: Currently the following options are supported:
@ -144,6 +163,8 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
filter=+filter1,-filter2,... filter=+filter1,-filter2,...
exclude_files=regex exclude_files=regex
linelength=80 linelength=80
root=subdir
headers=x,y,...
"set noparent" option prevents cpplint from traversing directory tree "set noparent" option prevents cpplint from traversing directory tree
upwards looking for more .cfg files in parent directories. This option upwards looking for more .cfg files in parent directories. This option
@ -159,6 +180,12 @@ Syntax: cpplint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...]
"linelength" allows to specify the allowed line length for the project. "linelength" allows to specify the allowed line length for the project.
The "root" option is similar in function to the --root flag (see example
above). Paths are relative to the directory of the CPPLINT.cfg.
The "headers" option is similar in function to the --headers flag
(see example above).
CPPLINT.cfg has an effect on files in the same directory and all CPPLINT.cfg has an effect on files in the same directory and all
sub-directories, unless overridden by a nested configuration file. sub-directories, unless overridden by a nested configuration file.
@ -525,10 +552,7 @@ _error_suppressions = {}
# The root directory used for deriving header guard CPP variable. # The root directory used for deriving header guard CPP variable.
# This is set by --root flag. # This is set by --root flag.
_root = None _root = None
_root_debug = False
# The project root directory. Used for deriving header guard CPP variable.
# This is set by --project_root flag. Must be an absolute path.
_project_root = None
# The allowed line length of files. # The allowed line length of files.
# This is set by --linelength flag. # This is set by --linelength flag.
@ -538,10 +562,25 @@ _line_length = 80
# This is set by --extensions flag. # This is set by --extensions flag.
_valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh']) _valid_extensions = set(['cc', 'h', 'cpp', 'cu', 'cuh'])
# Treat all headers starting with 'h' equally: .h, .hpp, .hxx etc.
# This is set by --headers flag.
_hpp_headers = set(['h'])
# {str, bool}: a map from error categories to booleans which indicate if the # {str, bool}: a map from error categories to booleans which indicate if the
# category should be suppressed for every line. # category should be suppressed for every line.
_global_error_suppressions = {} _global_error_suppressions = {}
def ProcessHppHeadersOption(val):
global _hpp_headers
try:
_hpp_headers = set(val.split(','))
# Automatically append to extensions list so it does not have to be set 2 times
_valid_extensions.update(_hpp_headers)
except ValueError:
PrintUsage('Header extensions must be comma separated list.')
def IsHeaderExtension(file_extension):
return file_extension in _hpp_headers
def ParseNolintSuppressions(filename, raw_line, linenum, error): def ParseNolintSuppressions(filename, raw_line, linenum, error):
"""Updates the global list of line error-suppressions. """Updates the global list of line error-suppressions.
@ -834,6 +873,7 @@ class _CppLintState(object):
self._filters_backup = self.filters[:] self._filters_backup = self.filters[:]
self.counting = 'total' # In what way are we counting errors? self.counting = 'total' # In what way are we counting errors?
self.errors_by_category = {} # string to int dict storing error counts self.errors_by_category = {} # string to int dict storing error counts
self.quiet = False # Suppress non-error messagess?
# output format: # output format:
# "emacs" - format that emacs can parse (default) # "emacs" - format that emacs can parse (default)
@ -844,6 +884,12 @@ class _CppLintState(object):
"""Sets the output format for errors.""" """Sets the output format for errors."""
self.output_format = output_format self.output_format = output_format
def SetQuiet(self, quiet):
"""Sets the module's quiet settings, and returns the previous setting."""
last_quiet = self.quiet
self.quiet = quiet
return last_quiet
def SetVerboseLevel(self, level): def SetVerboseLevel(self, level):
"""Sets the module's verbosity, and returns the previous setting.""" """Sets the module's verbosity, and returns the previous setting."""
last_verbose_level = self.verbose_level last_verbose_level = self.verbose_level
@ -911,7 +957,7 @@ class _CppLintState(object):
for category, count in self.errors_by_category.iteritems(): for category, count in self.errors_by_category.iteritems():
sys.stderr.write('Category \'%s\' errors found: %d\n' % sys.stderr.write('Category \'%s\' errors found: %d\n' %
(category, count)) (category, count))
sys.stderr.write('Total errors found: %d\n' % self.error_count) sys.stdout.write('Total errors found: %d\n' % self.error_count)
_cpplint_state = _CppLintState() _cpplint_state = _CppLintState()
@ -925,6 +971,14 @@ def _SetOutputFormat(output_format):
"""Sets the module's output format.""" """Sets the module's output format."""
_cpplint_state.SetOutputFormat(output_format) _cpplint_state.SetOutputFormat(output_format)
def _Quiet():
"""Return's the module's quiet setting."""
return _cpplint_state.quiet
def _SetQuiet(quiet):
"""Set the module's quiet status, and return previous setting."""
return _cpplint_state.SetQuiet(quiet)
def _VerboseLevel(): def _VerboseLevel():
"""Returns the module's verbosity setting.""" """Returns the module's verbosity setting."""
@ -1071,10 +1125,6 @@ class FileInfo(object):
if os.path.exists(fullname): if os.path.exists(fullname):
project_dir = os.path.dirname(fullname) project_dir = os.path.dirname(fullname)
if _project_root:
prefix = os.path.commonprefix([_project_root, project_dir])
return fullname[len(prefix) + 1:]
if os.path.exists(os.path.join(project_dir, ".svn")): if os.path.exists(os.path.join(project_dir, ".svn")):
# If there's a .svn file in the current directory, we recursively look # If there's a .svn file in the current directory, we recursively look
# up the directory tree for the top of the SVN checkout # up the directory tree for the top of the SVN checkout
@ -1089,12 +1139,13 @@ class FileInfo(object):
# Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by # Not SVN <= 1.6? Try to find a git, hg, or svn top level directory by
# searching up from the current path. # searching up from the current path.
root_dir = os.path.dirname(fullname) root_dir = current_dir = os.path.dirname(fullname)
while (root_dir != os.path.dirname(root_dir) and while current_dir != os.path.dirname(current_dir):
not os.path.exists(os.path.join(root_dir, ".git")) and if (os.path.exists(os.path.join(current_dir, ".git")) or
not os.path.exists(os.path.join(root_dir, ".hg")) and os.path.exists(os.path.join(current_dir, ".hg")) or
not os.path.exists(os.path.join(root_dir, ".svn"))): os.path.exists(os.path.join(current_dir, ".svn"))):
root_dir = os.path.dirname(root_dir) root_dir = current_dir
current_dir = os.path.dirname(current_dir)
if (os.path.exists(os.path.join(root_dir, ".git")) or if (os.path.exists(os.path.join(root_dir, ".git")) or
os.path.exists(os.path.join(root_dir, ".hg")) or os.path.exists(os.path.join(root_dir, ".hg")) or
@ -1189,8 +1240,8 @@ def Error(filename, linenum, category, confidence, message):
if _ShouldPrintError(category, confidence, linenum): if _ShouldPrintError(category, confidence, linenum):
_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): error cpplint: [%s] %s [%d]\n' % (
filename, linenum, message, category, confidence)) filename, linenum, category, message, confidence))
elif _cpplint_state.output_format == 'eclipse': elif _cpplint_state.output_format == 'eclipse':
sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % (
filename, linenum, message, category, confidence)) filename, linenum, message, category, confidence))
@ -1732,6 +1783,30 @@ def GetIndentLevel(line):
else: else:
return 0 return 0
def PathSplitToList(path):
"""Returns the path split into a list by the separator.
Args:
path: An absolute or relative path (e.g. '/a/b/c/' or '../a')
Returns:
A list of path components (e.g. ['a', 'b', 'c]).
"""
lst = []
while True:
(head, tail) = os.path.split(path)
if head == path: # absolute paths end
lst.append(head)
break
if tail == path: # relative paths end
lst.append(tail)
break
path = head
lst.append(tail)
lst.reverse()
return lst
def GetHeaderGuardCPPVariable(filename): def GetHeaderGuardCPPVariable(filename):
"""Returns the CPP variable that should be used as a header guard. """Returns the CPP variable that should be used as a header guard.
@ -1754,8 +1829,58 @@ def GetHeaderGuardCPPVariable(filename):
fileinfo = FileInfo(filename) fileinfo = FileInfo(filename)
file_path_from_root = fileinfo.RepositoryName() file_path_from_root = fileinfo.RepositoryName()
if _root:
file_path_from_root = re.sub('^' + _root + os.sep, '', file_path_from_root) def FixupPathFromRoot():
if _root_debug:
sys.stderr.write("\n_root fixup, _root = '%s', repository name = '%s'\n"
%(_root, fileinfo.RepositoryName()))
# Process the file path with the --root flag if it was set.
if not _root:
if _root_debug:
sys.stderr.write("_root unspecified\n")
return file_path_from_root
def StripListPrefix(lst, prefix):
# f(['x', 'y'], ['w, z']) -> None (not a valid prefix)
if lst[:len(prefix)] != prefix:
return None
# f(['a, 'b', 'c', 'd'], ['a', 'b']) -> ['c', 'd']
return lst[(len(prefix)):]
# root behavior:
# --root=subdir , lstrips subdir from the header guard
maybe_path = StripListPrefix(PathSplitToList(file_path_from_root),
PathSplitToList(_root))
if _root_debug:
sys.stderr.write(("_root lstrip (maybe_path=%s, file_path_from_root=%s," +
" _root=%s)\n") %(maybe_path, file_path_from_root, _root))
if maybe_path:
return os.path.join(*maybe_path)
# --root=.. , will prepend the outer directory to the header guard
full_path = fileinfo.FullName()
root_abspath = os.path.abspath(_root)
maybe_path = StripListPrefix(PathSplitToList(full_path),
PathSplitToList(root_abspath))
if _root_debug:
sys.stderr.write(("_root prepend (maybe_path=%s, full_path=%s, " +
"root_abspath=%s)\n") %(maybe_path, full_path, root_abspath))
if maybe_path:
return os.path.join(*maybe_path)
if _root_debug:
sys.stderr.write("_root ignore, returning %s\n" %(file_path_from_root))
# --root=FAKE_DIR is ignored
return file_path_from_root
file_path_from_root = FixupPathFromRoot()
return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_'
@ -3864,6 +3989,14 @@ def CheckTrailingSemicolon(filename, clean_lines, linenum, error):
# outputting warnings for the matching closing brace, if there are # outputting warnings for the matching closing brace, if there are
# nested blocks with trailing semicolons, we will get the error # nested blocks with trailing semicolons, we will get the error
# messages in reversed order. # messages in reversed order.
# We need to check the line forward for NOLINT
raw_lines = clean_lines.raw_lines
ParseNolintSuppressions(filename, raw_lines[endlinenum-1], endlinenum-1,
error)
ParseNolintSuppressions(filename, raw_lines[endlinenum], endlinenum,
error)
error(filename, endlinenum, 'readability/braces', 4, error(filename, endlinenum, 'readability/braces', 4,
"You don't need a ; after a }") "You don't need a ; after a }")
@ -4161,6 +4294,16 @@ def GetLineWidth(line):
if unicodedata.east_asian_width(uc) in ('W', 'F'): if unicodedata.east_asian_width(uc) in ('W', 'F'):
width += 2 width += 2
elif not unicodedata.combining(uc): elif not unicodedata.combining(uc):
# Issue 337
# https://mail.python.org/pipermail/python-list/2012-August/628809.html
if (sys.version_info.major, sys.version_info.minor) <= (3, 2):
# https://github.com/python/cpython/blob/2.7/Include/unicodeobject.h#L81
is_wide_build = sysconfig.get_config_var("Py_UNICODE_SIZE") >= 4
# https://github.com/python/cpython/blob/2.7/Objects/unicodeobject.c#L564
is_low_surrogate = 0xDC00 <= ord(uc) <= 0xDFFF
if not is_wide_build and is_low_surrogate:
width -= 1
width += 1 width += 1
return width return width
else: else:
@ -4234,7 +4377,7 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
# Check if the line is a header guard. # Check if the line is a header guard.
is_header_guard = False is_header_guard = False
if file_extension == 'h': if IsHeaderExtension(file_extension):
cppvar = GetHeaderGuardCPPVariable(filename) cppvar = GetHeaderGuardCPPVariable(filename)
if (line.startswith('#ifndef %s' % cppvar) or if (line.startswith('#ifndef %s' % cppvar) or
line.startswith('#define %s' % cppvar) or line.startswith('#define %s' % cppvar) or
@ -4583,7 +4726,7 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
CheckGlobalStatic(filename, clean_lines, linenum, error) CheckGlobalStatic(filename, clean_lines, linenum, error)
CheckPrintf(filename, clean_lines, linenum, error) CheckPrintf(filename, clean_lines, linenum, error)
if file_extension == 'h': if IsHeaderExtension(file_extension):
# TODO(unknown): check that 1-arg constructors are explicit. # TODO(unknown): check that 1-arg constructors are explicit.
# How to tell it's a constructor? # How to tell it's a constructor?
# (handled in CheckForNonStandardConstructs for now) # (handled in CheckForNonStandardConstructs for now)
@ -4690,12 +4833,12 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
# Check for use of unnamed namespaces in header files. Registration # Check for use of unnamed namespaces in header files. Registration
# macros are typically OK, so we allow use of "namespace {" on lines # macros are typically OK, so we allow use of "namespace {" on lines
# that end with backslashes. # that end with backslashes.
if (file_extension == 'h' if (IsHeaderExtension(file_extension)
and Search(r'\bnamespace\s*{', line) and Search(r'\bnamespace\s*{', line)
and line[-1] != '\\'): and line[-1] != '\\'):
error(filename, linenum, 'build/namespaces', 4, error(filename, linenum, 'build/namespaces', 4,
'Do not use unnamed namespaces in header files. See ' 'Do not use unnamed namespaces in header files. See '
'https://google.github.io/styleguide/cppguide.html#Namespaces' 'https://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Namespaces'
' for more information.') ' for more information.')
@ -5780,7 +5923,7 @@ def ProcessFileData(filename, file_extension, lines, error,
RemoveMultiLineComments(filename, lines, error) RemoveMultiLineComments(filename, lines, error)
clean_lines = CleansedLines(lines) clean_lines = CleansedLines(lines)
if file_extension == 'h': if IsHeaderExtension(file_extension):
CheckForHeaderGuard(filename, clean_lines, error) CheckForHeaderGuard(filename, clean_lines, error)
for line in xrange(clean_lines.NumLines()): for line in xrange(clean_lines.NumLines()):
@ -5849,6 +5992,9 @@ def ProcessConfigOverrides(filename):
if base_name: if base_name:
pattern = re.compile(val) pattern = re.compile(val)
if pattern.match(base_name): if pattern.match(base_name):
if _cpplint_state.quiet:
# Suppress "Ignoring file" warning when using --quiet.
return False
sys.stderr.write('Ignoring "%s": file excluded by "%s". ' sys.stderr.write('Ignoring "%s": file excluded by "%s". '
'File path component "%s" matches ' 'File path component "%s" matches '
'pattern "%s"\n' % 'pattern "%s"\n' %
@ -5860,6 +6006,12 @@ def ProcessConfigOverrides(filename):
_line_length = int(val) _line_length = int(val)
except ValueError: except ValueError:
sys.stderr.write('Line length must be numeric.') sys.stderr.write('Line length must be numeric.')
elif name == 'root':
global _root
# root directories are specified relative to CPPLINT.cfg dir.
_root = os.path.join(os.path.dirname(cfg_file), val)
elif name == 'headers':
ProcessHppHeadersOption(val)
else: else:
sys.stderr.write( sys.stderr.write(
'Invalid configuration option (%s) in file %s\n' % 'Invalid configuration option (%s) in file %s\n' %
@ -5894,6 +6046,7 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
_SetVerboseLevel(vlevel) _SetVerboseLevel(vlevel)
_BackupFilters() _BackupFilters()
old_errors = _cpplint_state.error_count
if not ProcessConfigOverrides(filename): if not ProcessConfigOverrides(filename):
_RestoreFilters() _RestoreFilters()
@ -5962,7 +6115,10 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
Error(filename, linenum, 'whitespace/newline', 1, Error(filename, linenum, 'whitespace/newline', 1,
'Unexpected \\r (^M) found; better to use only \\n') 'Unexpected \\r (^M) found; better to use only \\n')
sys.stderr.write('Done processing %s\n' % filename) # Suppress printing anything if --quiet was passed unless the error
# count has increased after processing this file.
if not _cpplint_state.quiet or old_errors != _cpplint_state.error_count:
sys.stdout.write('Done processing %s\n' % filename)
_RestoreFilters() _RestoreFilters()
@ -6006,13 +6162,15 @@ def ParseArguments(args):
'root=', 'root=',
'linelength=', 'linelength=',
'extensions=', 'extensions=',
'project_root=']) 'headers=',
'quiet'])
except getopt.GetoptError: except getopt.GetoptError:
PrintUsage('Invalid arguments.') PrintUsage('Invalid arguments.')
verbosity = _VerboseLevel() verbosity = _VerboseLevel()
output_format = _OutputFormat() output_format = _OutputFormat()
filters = '' filters = ''
quiet = _Quiet()
counting_style = '' counting_style = ''
for (opt, val) in opts: for (opt, val) in opts:
@ -6022,6 +6180,8 @@ def ParseArguments(args):
if val not in ('emacs', 'vs7', 'eclipse'): if val not in ('emacs', 'vs7', 'eclipse'):
PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.') PrintUsage('The only allowed output formats are emacs, vs7 and eclipse.')
output_format = val output_format = val
elif opt == '--quiet':
quiet = True
elif opt == '--verbose': elif opt == '--verbose':
verbosity = int(val) verbosity = int(val)
elif opt == '--filter': elif opt == '--filter':
@ -6035,11 +6195,6 @@ def ParseArguments(args):
elif opt == '--root': elif opt == '--root':
global _root global _root
_root = val _root = val
elif opt == '--project_root':
global _project_root
_project_root = val
if not os.path.isabs(_project_root):
PrintUsage('Project root must be an absolute path.')
elif opt == '--linelength': elif opt == '--linelength':
global _line_length global _line_length
try: try:
@ -6052,11 +6207,14 @@ def ParseArguments(args):
_valid_extensions = set(val.split(',')) _valid_extensions = set(val.split(','))
except ValueError: except ValueError:
PrintUsage('Extensions must be comma separated list.') PrintUsage('Extensions must be comma separated list.')
elif opt == '--headers':
ProcessHppHeadersOption(val)
if not filenames: if not filenames:
PrintUsage('No files were specified.') PrintUsage('No files were specified.')
_SetOutputFormat(output_format) _SetOutputFormat(output_format)
_SetQuiet(quiet)
_SetVerboseLevel(verbosity) _SetVerboseLevel(verbosity)
_SetFilters(filters) _SetFilters(filters)
_SetCountingStyle(counting_style) _SetCountingStyle(counting_style)
@ -6077,7 +6235,9 @@ def main():
_cpplint_state.ResetErrorCounts() _cpplint_state.ResetErrorCounts()
for filename in filenames: for filename in filenames:
ProcessFile(filename, _cpplint_state.verbose_level) ProcessFile(filename, _cpplint_state.verbose_level)
_cpplint_state.PrintErrorCounts() # If --quiet is passed, suppress printing error count unless there are errors.
if not _cpplint_state.quiet or _cpplint_state.error_count > 0:
_cpplint_state.PrintErrorCounts()
sys.exit(_cpplint_state.error_count > 0) sys.exit(_cpplint_state.error_count > 0)

Loading…
Cancel
Save