diff --git a/tools/cpplint.py b/tools/cpplint.py index 5f5f1400d7ec56..17f341a61d7a2c 100755 --- a/tools/cpplint.py +++ b/tools/cpplint.py @@ -45,7 +45,6 @@ import copy import getopt import glob -import logging import itertools import math # for log import os @@ -56,10 +55,6 @@ import unicodedata import xml.etree.ElementTree -try: - xrange -except NameError: - xrange = range # if empty, use defaults _header_extensions = set([]) @@ -73,7 +68,7 @@ # option (also supported in CPPLINT.cfg) def GetHeaderExtensions(): if not _header_extensions: - return set(['h', 'hpp', 'hxx', 'h++', 'cuh']) + return set(['h', 'hh', 'hpp', 'hxx', 'h++', 'cuh']) return _header_extensions # The allowed extensions for file names @@ -85,7 +80,6 @@ def GetAllExtensions(): def GetNonHeaderExtensions(): return GetAllExtensions().difference(GetHeaderExtensions()) -logger = logging.getLogger('testrunner') _USAGE = """ @@ -95,7 +89,6 @@ def GetNonHeaderExtensions(): [--root=subdir] [--linelength=digits] [--recursive] [--exclude=path] [--headers=ext1,ext2] - [--logfile=filename] [--extensions=hpp,cpp,...] [file] ... @@ -289,6 +282,7 @@ def GetNonHeaderExtensions(): 'build/forward_decl', 'build/header_guard', 'build/include', + 'build/include_subdir', 'build/include_alpha', 'build/include_order', 'build/include_what_you_use', @@ -304,11 +298,13 @@ def GetNonHeaderExtensions(): 'readability/constructors', 'readability/fn_size', 'readability/inheritance', + 'readability/pointer_notation', 'readability/multiline_comment', 'readability/multiline_string', 'readability/namespace', 'readability/nolint', 'readability/nul', + 'readability/null_usage', 'readability/strings', 'readability/todo', 'readability/utf8', @@ -361,8 +357,6 @@ def GetNonHeaderExtensions(): # All entries here should start with a '-' or '+', as in the --filter= flag. _DEFAULT_FILTERS = [ '-build/include', - '-build/include_alpha', - '-build/include_order', '-build/include_subdir', '-legal/copyright', ] @@ -489,6 +483,18 @@ def GetNonHeaderExtensions(): 'utility', 'valarray', 'vector', + # 17.6.1.2 C++14 headers + 'shared_mutex', + # 17.6.1.2 C++17 headers + 'any', + 'charconv', + 'codecvt', + 'execution', + 'filesystem', + 'memory_resource', + 'optional', + 'string_view', + 'variant', # 17.6.1.2 C++ headers for C library facilities 'cassert', 'ccomplex', @@ -696,6 +702,8 @@ def unicode_escape_decode(x): _global_error_suppressions = {} + + def ParseNolintSuppressions(filename, raw_line, linenum, error): """Updates the global list of line error-suppressions. @@ -1278,7 +1286,7 @@ def FullName(self): return os.path.abspath(self._filename).replace('\\', '/') def RepositoryName(self): - """FullName after removing the local path to the repository. + r"""FullName after removing the local path to the repository. If we have a real absolute path name here we can try to do something smart: detecting the root of the checkout and truncating /path/to/checkout from @@ -1289,10 +1297,11 @@ def RepositoryName(self): """ fullname = self.FullName() # XXX(bnoordhuis) Expects that cpplint.py lives in the tools/ directory. - toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) \ - .replace('\\', '/').decode('utf-8') + toplevel = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')).replace('\\', '/') + toplevel = unicode_escape_decode(toplevel) prefix = os.path.commonprefix([fullname, toplevel]) return fullname[len(prefix) + 1:] + # End Node.js patch def Split(self): """Splits the file into the directory, basename, and extension. @@ -1313,7 +1322,7 @@ def BaseName(self): return self.Split()[1] def Extension(self): - """File extension - text following the final period.""" + """File extension - text following the final period, includes that period.""" return self.Split()[2] def NoExtension(self): @@ -1383,15 +1392,9 @@ def Error(filename, linenum, category, confidence, message): elif _cpplint_state.output_format == 'eclipse': sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) - elif _cpplint_state.output_format == 'tap': - template = ('not ok %(filename)s\n' - ' ---\n' - ' message: %(message)s\n' - ' data:\n' - ' line: %(linenum)d\n' - ' ruleId: %(category)s\n' - ' ...') - logger.info(template % locals()) + elif _cpplint_state.output_format == 'junit': + _cpplint_state.AddJUnitFailure(filename, linenum, message, category, + confidence) else: final_message = '%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence) @@ -1907,7 +1910,7 @@ def CheckForCopyright(filename, lines, error): # We'll say it should occur by line 10. Don't forget there's a # dummy line at the front. - for line in xrange(1, min(len(lines), 11)): + for line in range(1, min(len(lines), 11)): if re.search(r'Copyright', lines[line], re.I): break else: # means no copyright line was found error(filename, 0, 'legal/copyright', 5, @@ -1953,12 +1956,10 @@ def GetHeaderGuardCPPVariable(filename): fileinfo = FileInfo(filename) file_path_from_root = fileinfo.RepositoryName() if _root: - suffix = os.sep - # On Windows using directory separator will leave us with - # "bogus escape error" unless we properly escape regex. - if suffix == '\\': - suffix += '\\' - file_path_from_root = re.sub('^' + _root + suffix, '', file_path_from_root) + # Convert root path to unix format because file_path_from_root is also + # in that format and they wouldn't match otherwise on Windows machines + root = os.path.normpath(_root).replace('\\', '/') + file_path_from_root = re.sub('^' + root + '/', '', file_path_from_root) return re.sub(r'[^a-zA-Z0-9]', '_', file_path_from_root).upper() + '_' @@ -2074,22 +2075,22 @@ def CheckHeaderFileIncluded(filename, include_state, error): return for ext in GetHeaderExtensions(): - basefilename = filename[0:len(filename) - len(fileinfo.Extension())] - headerfile = basefilename + '.' + ext - if not os.path.exists(headerfile): - continue - headername = FileInfo(headerfile).RepositoryName() - first_include = None - for section_list in include_state.include_list: - for f in section_list: - if headername in f[0] or f[0] in headername: - return - if not first_include: - first_include = f[1] + basefilename = filename[0:len(filename) - len(fileinfo.Extension())] + headerfile = basefilename + '.' + ext + if not os.path.exists(headerfile): + continue + headername = FileInfo(headerfile).RepositoryName() + first_include = None + for section_list in include_state.include_list: + for f in section_list: + if headername in f[0] or f[0] in headername: + return + if not first_include: + first_include = f[1] - error(filename, first_include, 'build/include', 5, - '%s should include its header file %s' % (fileinfo.RepositoryName(), - headername)) + error(filename, first_include, 'build/include', 5, + '%s should include its header file %s' % (fileinfo.RepositoryName(), + headername)) def CheckForBadCharacters(filename, lines, error): @@ -3188,7 +3189,7 @@ def CheckForFunctionLengths(filename, clean_lines, linenum, if starting_func: body_found = False - for start_linenum in xrange(linenum, clean_lines.NumLines()): + for start_linenum in range(linenum, clean_lines.NumLines()): start_line = lines[start_linenum] joined_line += ' ' + start_line.lstrip() if Search(r'(;|})', start_line): # Declarations and trivial functions @@ -4450,7 +4451,7 @@ def CheckLeftLeaningPointer(filename, clean_lines, linenum, error): return for match in _RIGHT_LEANING_POINTER_PATTERN.finditer(line): - error(filename, linenum, 'readability/null_usage', 2, + error(filename, linenum, 'readability/pointer_notation', 2, 'Use left leaning pointer instead of right leaning') def GetLineWidth(line): @@ -4677,7 +4678,7 @@ def _ClassifyInclude(fileinfo, include, is_system): # Headers with C++ extensions shouldn't be considered C system headers if is_system and os.path.splitext(include)[1] in ['.hpp', '.hxx', '.h++']: - is_system = False + is_system = False if is_system: if is_cpp_h: @@ -4911,8 +4912,6 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, if match: include_state.ResetSection(match.group(1)) - # Make Windows paths like Unix. - fullname = os.path.abspath(filename).replace('\\', '/') # Perform other checks now that we are sure that this is not an include line CheckCasts(filename, clean_lines, linenum, error) @@ -5565,12 +5564,15 @@ def ExpectingFunctionArgs(clean_lines, linenum): ('', ('numeric_limits',)), ('', ('list',)), ('', ('map', 'multimap',)), - ('', ('allocator',)), + ('', ('allocator', 'make_shared', 'make_unique', 'shared_ptr', + 'unique_ptr', 'weak_ptr')), ('', ('queue', 'priority_queue',)), ('', ('set', 'multiset',)), ('', ('stack',)), ('', ('char_traits', 'basic_string',)), ('', ('tuple',)), + ('', ('unordered_map', 'unordered_multimap')), + ('', ('unordered_set', 'unordered_multiset')), ('', ('pair',)), ('', ('vector',)), @@ -5585,7 +5587,7 @@ def ExpectingFunctionArgs(clean_lines, linenum): ('', ('copy', 'max', 'min', 'min_element', 'sort', 'transform', )), - ('', ('swap',)), + ('', ('forward', 'make_pair', 'move', 'swap')), ) _RE_PATTERN_STRING = re.compile(r'\bstring\b') @@ -5716,7 +5718,7 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, required = {} # A map of header name to linenumber and the template entity. # Example of required: { '': (1219, 'less<>') } - for linenum in xrange(clean_lines.NumLines()): + for linenum in range(clean_lines.NumLines()): line = clean_lines.elided[linenum] if not line or line[0] == '#': continue @@ -5739,8 +5741,13 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, continue for pattern, template, header in _re_pattern_templates: - if pattern.search(line): - required[header] = (linenum, template) + matched = pattern.search(line) + if matched: + # Don't warn about IWYU in non-STL namespaces: + # (We check only the first match per line; good enough.) + prefix = line[:matched.start()] + if prefix.endswith('std::') or not prefix.endswith('::'): + required[header] = (linenum, template) # The policy is that if you #include something in foo.h you don't need to # include it again in foo.cc. Here, we will look at possible includes. @@ -6120,7 +6127,7 @@ def ProcessFileData(filename, file_extension, lines, error, if file_extension in GetHeaderExtensions(): CheckForHeaderGuard(filename, clean_lines, error) - for line in xrange(clean_lines.NumLines()): + for line in range(clean_lines.NumLines()): ProcessLine(filename, file_extension, clean_lines, line, include_state, function_state, nesting_state, error, extra_check_functions) @@ -6190,7 +6197,7 @@ def ProcessConfigOverrides(filename): if pattern.match(base_name): _cpplint_state.PrintInfo('Ignoring "%s": file excluded by ' '"%s". File path component "%s" matches pattern "%s"\n' % - (filename, cfg_file, base_name, val)) + (filename, cfg_file, base_name, val)) return False elif name == 'linelength': global _line_length @@ -6363,7 +6370,6 @@ def ParseArguments(args): (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=', 'counting=', 'filter=', - 'logfile=', 'root=', 'repository=', 'linelength=', @@ -6385,9 +6391,9 @@ def ParseArguments(args): if opt == '--help': PrintUsage(None) elif opt == '--output': - if val not in ('emacs', 'vs7', 'eclipse', 'junit', 'tap'): - PrintUsage( - 'The only allowed output formats are emacs, vs7, eclipse, junit and tap.') + if val not in ('emacs', 'vs7', 'eclipse', 'junit'): + PrintUsage('The only allowed output formats are emacs, vs7, eclipse ' + 'and junit.') output_format = val elif opt == '--verbose': verbosity = int(val) @@ -6408,9 +6414,9 @@ def ParseArguments(args): elif opt == '--linelength': global _line_length try: - _line_length = int(val) + _line_length = int(val) except ValueError: - PrintUsage('Line length must be digits.') + PrintUsage('Line length must be digits.') elif opt == '--exclude': global _excludes if not _excludes: @@ -6419,7 +6425,7 @@ def ParseArguments(args): elif opt == '--extensions': global _valid_extensions try: - _valid_extensions = set(val.split(',')) + _valid_extensions = set(val.split(',')) except ValueError: PrintUsage('Extensions must be comma seperated list.') elif opt == '--headers': @@ -6430,8 +6436,6 @@ def ParseArguments(args): PrintUsage('Extensions must be comma seperated list.') elif opt == '--recursive': recursive = True - elif opt == '--logfile': - logger.addHandler(logging.FileHandler(val, mode='wb')) elif opt == '--quiet': global _quiet _quiet = True @@ -6497,22 +6501,13 @@ def main(): try: # Change stderr to write with replacement characters so we don't die # if we try to print something containing non-ASCII characters. - sys.stderr = codecs.StreamReaderWriter(sys.stderr, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace') - - logger.addHandler(logging.StreamHandler(sys.stdout)) - logger.setLevel(logging.INFO) + sys.stderr = codecs.StreamReader(sys.stderr, 'replace') _cpplint_state.ResetErrorCounts() for filename in filenames: - ProcessFile(filename.decode('utf-8'), _cpplint_state.verbose_level) + ProcessFile(filename, _cpplint_state.verbose_level) _cpplint_state.PrintErrorCounts() - if _cpplint_state.output_format == 'tap': - logger.info('TAP version 13') - if _cpplint_state.output_format == 'junit': sys.stderr.write(_cpplint_state.FormatJUnitXML())