diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
commit | 339e2d15cc26fe86988ea06468d912a46c8d6f29 (patch) | |
tree | a6167fc8fcfc6ae2dc102f57b2473858eac34063 /src/clint.py | |
parent | 067dc73729267c0262438a6fdd66e586f8496946 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.gz rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.bz2 rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.zip |
Merge remote-tracking branch 'upstream/master' into fix_repeatcmdline
Diffstat (limited to 'src/clint.py')
-rwxr-xr-x | src/clint.py | 702 |
1 files changed, 146 insertions, 556 deletions
diff --git a/src/clint.py b/src/clint.py index 155f5f2ce5..1f588322f3 100755 --- a/src/clint.py +++ b/src/clint.py @@ -42,11 +42,10 @@ import copy import getopt import os import re -import sre_compile import string import sys import json -import collections # for defaultdict +import collections _USAGE = """ @@ -150,10 +149,9 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] # 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. _ERROR_CATEGORIES = [ - 'build/deprecated', 'build/endif_comment', 'build/header_guard', - 'build/include_alpha', + 'build/include_defs', 'build/printf_format', 'build/storage_class', 'readability/bool', @@ -170,14 +168,9 @@ _ERROR_CATEGORIES = [ 'runtime/printf_format', 'runtime/threadsafe_fn', 'runtime/deprecated', - 'syntax/parenthesis', - 'whitespace/alignment', - 'whitespace/braces', 'whitespace/comments', 'whitespace/indent', - 'whitespace/newline', 'whitespace/operators', - 'whitespace/parens', 'whitespace/todo', 'whitespace/cast', ] @@ -186,7 +179,7 @@ _ERROR_CATEGORIES = [ # flag. By default all errors are on, so only add here categories that should be # off by default (i.e., categories that must be enabled by the --filter= flags). # All entries here should start with a '-' or '+', as in the --filter= flag. -_DEFAULT_FILTERS = ['-build/include_alpha'] +_DEFAULT_FILTERS = [] # These constants define the current inline assembly state _NO_ASM = 0 # Outside of inline assembly block @@ -308,14 +301,14 @@ def Match(pattern, s): # performance reasons; factoring it out into a separate function turns out # to be noticeably expensive. if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + _regexp_compile_cache[pattern] = re.compile(pattern) return _regexp_compile_cache[pattern].match(s) def Search(pattern, s): """Searches the string for the pattern, caching the compiled regexp.""" if pattern not in _regexp_compile_cache: - _regexp_compile_cache[pattern] = sre_compile.compile(pattern) + _regexp_compile_cache[pattern] = re.compile(pattern) return _regexp_compile_cache[pattern].search(s) @@ -481,38 +474,6 @@ def _SetFilters(filters): _cpplint_state.SetFilters(filters) -class _FunctionState: - - """Tracks current function name and the number of lines in its body.""" - - _NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc. - _TEST_TRIGGER = 400 # about 50% more than _NORMAL_TRIGGER. - - def __init__(self): - self.in_a_function = False - self.lines_in_function = 0 - self.current_function = '' - - def Begin(self, function_name): - """Start analyzing function body. - - Args: - function_name: The name of the function being tracked. - """ - self.in_a_function = True - self.lines_in_function = 0 - self.current_function = function_name - - def Count(self): - """Count line in current function body.""" - if self.in_a_function: - self.lines_in_function += 1 - - def End(self): - """Stop analyzing function body.""" - self.in_a_function = False - - class FileInfo: """Provides utility functions for filenames. @@ -549,29 +510,6 @@ class FileInfo: # Don't know what to do; header guard warnings may be wrong... return fullname - def Split(self): - """Splits the file into the directory, basename, and extension. - - For 'chrome/browser/browser.cc', Split() would - return ('chrome/browser', 'browser', '.cc') - - Returns: - A tuple of (directory, basename, extension). - """ - - googlename = self.RelativePath() - project, rest = os.path.split(googlename) - return (project,) + os.path.splitext(rest) - - def BaseName(self): - """File base name - text after the final slash, before final period.""" - return self.Split()[1] - - def Extension(self): - """File extension - text following the final period.""" - return self.Split()[2] - - def _ShouldPrintError(category, confidence, linenum): """If confidence >= verbose, category passes filter and isn't suppressed.""" @@ -925,110 +863,8 @@ def CloseExpression(clean_lines, linenum, pos): return (line, clean_lines.NumLines(), -1) -def FindStartOfExpressionInLine(line, endpos, depth, startchar, endchar): - """Find position at the matching startchar. - - This is almost the reverse of FindEndOfExpressionInLine, but note - that the input position and returned position differs by 1. - - Args: - line: a CleansedLines line. - endpos: start searching at this position. - depth: nesting level at endpos. - startchar: expression opening character. - endchar: expression closing character. - - Returns: - On finding matching startchar: (index at matching startchar, 0) - Otherwise: (-1, new depth at beginning of this line) - """ - for i in range(endpos, -1, -1): - if line[i] == endchar: - depth += 1 - elif line[i] == startchar: - depth -= 1 - if depth == 0: - return (i, 0) - return (-1, depth) - - -def ReverseCloseExpression(clean_lines, linenum, pos): - """If input points to ) or } or ] or >, finds the position that opens it. - - If lines[linenum][pos] points to a ')' or '}' or ']' or '>', finds the - linenum/pos that correspond to the opening of the expression. - - Args: - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - pos: A position on the line. - - Returns: - A tuple (line, linenum, pos) pointer *at* the opening brace, or - (line, 0, -1) if we never find the matching opening brace. Note - we ignore strings and comments when matching; and the line we - return is the 'cleansed' line at linenum. - """ - line = clean_lines.elided[linenum] - endchar = line[pos] - startchar = None - if endchar not in ')}]>': - return (line, 0, -1) - if endchar == ')': - startchar = '(' - if endchar == ']': - startchar = '[' - if endchar == '}': - startchar = '{' - if endchar == '>': - startchar = '<' - - # Check last line - (start_pos, num_open) = FindStartOfExpressionInLine( - line, pos, 0, startchar, endchar) - if start_pos > -1: - return (line, linenum, start_pos) - - # Continue scanning backward - while linenum > 0: - linenum -= 1 - line = clean_lines.elided[linenum] - (start_pos, num_open) = FindStartOfExpressionInLine( - line, len(line) - 1, num_open, startchar, endchar) - if start_pos > -1: - return (line, linenum, start_pos) - - # Did not find startchar before beginning of file, give up - return (line, 0, -1) - - -def GetHeaderGuardCPPVariable(filename): - """Returns the CPP variable that should be used as a header guard. - - Args: - filename: The name of a C++ header file. - - Returns: - The CPP variable that should be used as a header guard in the - named file. - - """ - - # Restores original filename in case that cpplint is invoked from Emacs's - # flymake. - filename = re.sub(r'_flymake\.h$', '.h', filename) - filename = re.sub(r'/\.flymake/([^/]*)$', r'/\1', filename) - - fileinfo = FileInfo(filename) - file_path_from_root = fileinfo.RelativePath() - return 'NVIM_' + re.sub(r'[-./\s]', '_', file_path_from_root).upper() - - def CheckForHeaderGuard(filename, lines, error): - """Checks that the file contains a header guard. - - Logs an error if no #ifndef header guard is present. For other - headers, checks that the full pathname is used. + """Checks that the file contains "#pragma once". Args: filename: The name of the C++ header file. @@ -1040,65 +876,133 @@ def CheckForHeaderGuard(filename, lines, error): }: return - cppvar = GetHeaderGuardCPPVariable(filename) - - ifndef = None - ifndef_linenum = 0 - define = None - endif = None - endif_linenum = 0 - for linenum, line in enumerate(lines): - linesplit = line.split() - if len(linesplit) >= 2: - # find the first occurrence of #ifndef and #define, save arg - if not ifndef and linesplit[0] == '#ifndef': - # set ifndef to the header guard presented on the #ifndef line. - ifndef = linesplit[1] - ifndef_linenum = linenum - if not define and linesplit[0] == '#define': - define = linesplit[1] - # find the last occurrence of #endif, save entire line - if line.startswith('#endif'): - endif = line - endif_linenum = linenum - - if not ifndef: - error(filename, 0, 'build/header_guard', 5, - 'No #ifndef header guard found, suggested CPP variable is: %s' % - cppvar) - return - - if not define: + if "#pragma once" not in lines: error(filename, 0, 'build/header_guard', 5, - 'No #define header guard found, suggested CPP variable is: %s' % - cppvar) - return + 'No "#pragma once" found in header') - # The guard should be PATH_FILE_H_, but we also allow PATH_FILE_H__ - # for backward compatibility. - if ifndef != cppvar: - error_level = 0 - if ifndef != cppvar + '_': - error_level = 5 +def CheckIncludes(filename, lines, error): + """Checks that headers only include _defs headers - ParseNolintSuppressions(lines[ifndef_linenum], ifndef_linenum) - error(filename, ifndef_linenum, 'build/header_guard', error_level, - '#ifndef header guard has wrong style, please use: %s' % cppvar) - - if define != ifndef: - error(filename, 0, 'build/header_guard', 5, - '#ifndef and #define don\'t match, suggested CPP variable is: %s' - % cppvar) + Args: + filename: The name of the C++ header file. + lines: An array of strings, each representing a line of the file. + error: The function to call with any errors found. + """ + if filename.endswith('.c.h') or filename.endswith('.in.h') or FileInfo(filename).RelativePath() in { + 'func_attr.h', + 'os/pty_process.h', + }: return - if endif != ('#endif // %s' % cppvar): - error_level = 0 - if endif != ('#endif // %s' % (cppvar + '_')): - error_level = 5 + # These should be synced with the ignored headers in the `iwyu` target in + # the Makefile. + check_includes_ignore = [ + "src/nvim/api/extmark.h", + "src/nvim/api/private/dispatch.h", + "src/nvim/api/private/helpers.h", + "src/nvim/api/private/validate.h", + "src/nvim/api/ui.h", + "src/nvim/ascii_defs.h", + "src/nvim/assert_defs.h", + "src/nvim/autocmd.h", + "src/nvim/autocmd_defs.h", + "src/nvim/buffer.h", + "src/nvim/buffer_defs.h", + "src/nvim/channel.h", + "src/nvim/charset.h", + "src/nvim/cmdexpand.h", + "src/nvim/cmdhist.h", + "src/nvim/decoration.h", + "src/nvim/diff.h", + "src/nvim/drawline.h", + "src/nvim/drawscreen.h", + "src/nvim/eval.h", + "src/nvim/eval/encode.h", + "src/nvim/eval/typval.h", + "src/nvim/eval/typval_defs.h", + "src/nvim/eval/userfunc.h", + "src/nvim/eval/window.h", + "src/nvim/event/libuv_process.h", + "src/nvim/event/loop.h", + "src/nvim/event/multiqueue.h", + "src/nvim/event/process.h", + "src/nvim/event/rstream.h", + "src/nvim/event/signal.h", + "src/nvim/event/socket.h", + "src/nvim/event/stream.h", + "src/nvim/event/time.h", + "src/nvim/event/wstream.h", + "src/nvim/ex_cmds.h", + "src/nvim/ex_cmds_defs.h", + "src/nvim/ex_docmd.h", + "src/nvim/extmark.h", + "src/nvim/file_search.h", + "src/nvim/fileio.h", + "src/nvim/fold.h", + "src/nvim/garray.h", + "src/nvim/getchar.h", + "src/nvim/globals.h", + "src/nvim/grid.h", + "src/nvim/highlight.h", + "src/nvim/highlight_group.h", + "src/nvim/input.h", + "src/nvim/insexpand.h", + "src/nvim/keycodes.h", + "src/nvim/log.h", + "src/nvim/lua/executor.h", + "src/nvim/main.h", + "src/nvim/mark.h", + "src/nvim/mouse.h", + "src/nvim/move.h", + "src/nvim/msgpack_rpc/channel.h", + "src/nvim/msgpack_rpc/channel_defs.h", + "src/nvim/msgpack_rpc/helpers.h", + "src/nvim/msgpack_rpc/unpacker.h", + "src/nvim/option.h", + "src/nvim/os/fileio.h", + "src/nvim/os/input.h", + "src/nvim/os/pty_conpty_win.h", + "src/nvim/os/pty_process_unix.h", + "src/nvim/os/pty_process_win.h", + "src/nvim/path.h", + "src/nvim/plines.h", + "src/nvim/popupmenu.h", + "src/nvim/search.h", + "src/nvim/spell.h", + "src/nvim/syntax.h", + "src/nvim/textobject.h", + "src/nvim/tui/input.h", + "src/nvim/tui/tui.h", + "src/nvim/ui.h", + "src/nvim/ui_client.h", + "src/nvim/ui_compositor.h", + "src/nvim/viml/parser/expressions.h", + "src/nvim/viml/parser/parser.h", + "src/nvim/window.h", + ] + + skip_headers = [ + "klib/kvec.h", + "klib/klist.h", + "auto/config.h", + "nvim/func_attr.h" + ] + + for i in check_includes_ignore: + if filename.endswith(i): + return - ParseNolintSuppressions(lines[endif_linenum], endif_linenum) - error(filename, endif_linenum, 'build/header_guard', error_level, - '#endif line should be "#endif // %s"' % cppvar) + for i, line in enumerate(lines): + matched = Match(r'#\s*include\s*"([^"]*)"', line) + if matched: + name = matched.group(1) + if name in skip_headers: + continue + if (not name.endswith('.h.generated.h') and + not name.endswith('_defs.h') and + not name.endswith('/defs.h')): + error(filename, i, 'build/include_defs', 5, + 'Headers should not include non-"_defs" headers') def CheckForBadCharacters(filename, lines, error): @@ -1545,82 +1449,6 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum, error): error(filename, linenum, 'build/endif_comment', 5, 'Uncommented text after #endif is non-standard. Use a comment.') - if Search(r'(\w+|[+-]?\d+(\.\d*)?)\s*(<|>)\?=?\s*(\w+|[+-]?\d+)(\.\d*)?', - line): - error(filename, linenum, 'build/deprecated', 3, - '>? and <? (max and min) operators are' - ' non-standard and deprecated.') - - -def CheckSpacingForFunctionCall(filename, line, linenum, error): - """Checks for the correctness of various spacing around function calls. - - Args: - filename: The name of the current file. - line: The text of the line to check. - linenum: The number of the line to check. - error: The function to call with any errors found. - """ - - # Since function calls often occur inside if/for/while/switch - # expressions - which have their own, more liberal conventions - we - # first see if we should be looking inside such an expression for a - # function call, to which we can apply more strict standards. - fncall = line # if there's no control flow construct, look at whole line - for pattern in (r'\bif\s*\((.*)\)\s*{', - r'\bfor\s*\((.*)\)\s*{', - r'\bwhile\s*\((.*)\)\s*[{;]', - r'\bswitch\s*\((.*)\)\s*{'): - match = Search(pattern, line) - if match: - # look inside the parens for function calls - fncall = match.group(1) - break - - # Except in if/for/while/switch/case, there should never be space - # immediately inside parens (eg "f( 3, 4 )"). We make an exception - # for nested parens ( (a+b) + c ). Likewise, there should never be - # a space before a ( when it's a function argument. I assume it's a - # function argument when the char before the whitespace is legal in - # a function name (alnum + _) and we're not starting a macro. Also ignore - # pointers and references to arrays and functions coz they're too tricky: - # we use a very simple way to recognize these: - # " (something)(maybe-something)" or - # " (something)(maybe-something," or - # " (something)[something]" - # Note that we assume the contents of [] to be short enough that - # they'll never need to wrap. - if ( # Ignore control structures. - not Search(r'\b(if|for|while|switch|case|return|sizeof)\b', fncall) and - # Ignore pointers/references to functions. - not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and - # Ignore pointers/references to arrays. - not Search(r' \([^)]+\)\[[^\]]+\]', fncall)): - # a ( used for a fn call - if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space after ( in function call') - elif Search(r'\(\s+(?!(\s*\\)|\()', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space after (') - if (Search(r'\w\s+\(', fncall) and - not Search(r'#\s*define|typedef', fncall) and - not Search(r'\w\s+\((\w+::)*\*\w+\)\(', fncall)): - error(filename, linenum, 'whitespace/parens', 4, - 'Extra space before ( in function call') - # If the ) is followed only by a newline or a { + newline, assume it's - # part of a control statement (if/while/etc), and don't complain - if Search(r'[^)]\s+\)\s*[^{\s]', fncall): - # If the closing parenthesis is preceded by only whitespaces, - # try to give a more descriptive error message. - if Search(r'^\s+\)', fncall): - error(filename, linenum, 'whitespace/parens', 2, - 'Closing ) should be moved to the previous line') - else: - error(filename, linenum, 'whitespace/parens', 2, - 'Extra space before )') - - def IsBlankLine(line): """Returns true if the given line is blank. @@ -1636,75 +1464,6 @@ def IsBlankLine(line): return not line or line.isspace() -def CheckForFunctionLengths(filename, clean_lines, linenum, - function_state, error): - """Reports for long function bodies. - - For an overview why this is done, see: - http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Write_Short_Functions - - Uses a simplistic algorithm assuming other style guidelines - (especially spacing) are followed. - Only checks unindented functions, so class members are unchecked. - Trivial bodies are unchecked, so constructors with huge initializer lists - may be missed. - Blank/comment lines are not counted so as to avoid encouraging the removal - of vertical space and comments just to get through a lint check. - NOLINT *on the last line of a function* disables this check. - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - function_state: Current function name and lines in body so far. - error: The function to call with any errors found. - """ - lines = clean_lines.lines - line = lines[linenum] - joined_line = '' - - starting_func = False - regexp = r'(\w(\w|::|\*|\&|\s)*)\(' # decls * & space::name( ... - match_result = Match(regexp, line) - if match_result: - # If the name is all caps and underscores, figure it's a macro and - # ignore it, unless it's TEST or TEST_F. - function_name = match_result.group(1).split()[-1] - if function_name == 'TEST' or function_name == 'TEST_F' or ( - not Match(r'[A-Z_]+$', function_name)): - starting_func = True - - if starting_func: - body_found = False - for start_linenum in range(linenum, clean_lines.NumLines()): - start_line = lines[start_linenum] - joined_line += ' ' + start_line.lstrip() - # Declarations and trivial functions - if Search(r'(;|})', start_line): - body_found = True - break # ... ignore - elif Search(r'{', start_line): - body_found = True - function = Search(r'((\w|:)*)\(', line).group(1) - if Match(r'TEST', function): # Handle TEST... macros - parameter_regexp = Search(r'(\(.*\))', joined_line) - if parameter_regexp: # Ignore bad syntax - function += parameter_regexp.group(1) - else: - function += '()' - function_state.Begin(function) - break - if not body_found: - # No body for the function (or evidence of a non-function) was - # found. - error(filename, linenum, 'readability/fn_size', 5, - 'Lint failed to find start of function body.') - elif Match(r'^\}\s*$', line): # function end - function_state.End() - elif not Match(r'^\s*$', line): - function_state.Count() # Count non-blank/non-comment lines. - - _RE_PATTERN_TODO = re.compile(r'^//(\s*)TODO(\(.+?\))?(:?)(\s|$)?') @@ -1727,9 +1486,7 @@ def CheckComment(comment, filename, linenum, error): username = match.group(2) if not username: - error(filename, linenum, 'readability/todo', 2, - 'Missing username in TODO; it should look like ' - '"// TODO(my_username): Stuff."') + return colon = match.group(3) if not colon: @@ -1871,89 +1628,6 @@ def FindPreviousMatchingAngleBracket(clean_lines, linenum, init_prefix): # Exhausted all earlier lines and still no matching angle bracket. return False - -def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): - """Checks for the correctness of alignment inside expressions - - Args: - filename: The name of the current file. - clean_lines: A CleansedLines instance containing the file. - linenum: The number of the line to check. - error: The function to call with any errors found. - startpos: Position where to start searching for expression start. - """ - level_starts = {} - line = clean_lines.elided_with_space_strings[linenum] - prev_line_start = Search(r'\S', line).start() - depth_line_starts = {} - pos = min([ - idx - for idx in ( - line.find(k, startpos) - for k in BRACES - if k != '{' - ) - if idx >= 0 - ] + [len(line) + 1]) - if pos == len(line) + 1: - return - ignore_error_levels = set() - firstlinenum = linenum - for linenum, pos, brace, depth in GetExprBracesPosition( - clean_lines, linenum, pos - ): - line = clean_lines.elided_with_space_strings[linenum] - if depth is None: - if pos < len(line) - 1: - CheckExpressionAlignment(filename, clean_lines, linenum, error, - pos + 1) - return - elif depth <= 0: - error(filename, linenum, 'syntax/parenthesis', 4, - 'Unbalanced parenthesis') - return - if brace == 's': - assert firstlinenum != linenum - if level_starts[depth][1]: - if line[pos] == BRACES[depth_line_starts[depth][1]]: - if pos != depth_line_starts[depth][0]: - if depth not in ignore_error_levels: - error(filename, linenum, 'whitespace/indent', 2, - 'End of the inner expression should have ' - 'the same indent as start') - else: - if (pos != level_starts[depth][0] + 1 - + (level_starts[depth][2] == '{')): - if depth not in ignore_error_levels: - error(filename, linenum, 'whitespace/alignment', 2, - ('Inner expression should be aligned ' - 'as opening brace + 1 (+ 2 in case of {{). ' - 'Relevant opening is on line {0!r}').format( - level_starts[depth][3])) - prev_line_start = pos - elif brace == 'e': - pass - else: - opening = brace in BRACES - if opening: - # Only treat {} as part of the expression if it is preceded by - # "=" (brace initializer) or "(type)" (construct like (struct - # foo) { ... }). - if brace == '{' and not (Search( - r'(?:= *|\((?:struct )?\w+(\s*\[\w*\])?\)) *$', - line[:pos]) - ): - ignore_error_levels.add(depth) - line_ended_with_opening = ( - pos == len(line) - 2 * (line.endswith(' \\')) - 1) - level_starts[depth] = (pos, line_ended_with_opening, brace, - linenum) - if line_ended_with_opening: - depth_line_starts[depth] = (prev_line_start, brace) - else: - del level_starts[depth] - - def CheckSpacing(filename, clean_lines, linenum, error): """Checks for the correctness of various spacing issues in the code. @@ -2067,8 +1741,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): # sometimes people put non-spaces on one side when aligning ='s among # many lines (not that this is behavior that I approve of...) if Search(r'[\w.]=[\w.]', line) and not Search(r'\b(if|while) ', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Missing spaces around =') + return # It's ok not to have spaces around binary operators like + - * /, but if # there's too little whitespace, we get concerned. It's hard to tell, @@ -2085,14 +1758,11 @@ def CheckSpacing(filename, clean_lines, linenum, error): # check non-include lines for spacing around < and >. match = Search(r'[^<>=!\s](==|!=|<=|>=)[^<>=!\s]', line) if match: - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around %s' % match.group(1)) + return # Boolean operators should be placed on the next line. if Search(r'(?:&&|\|\|)$', line): - error(filename, linenum, 'whitespace/operators', 4, - 'Boolean operator should be placed on the same line as the start ' - 'of its right operand') + return # We allow no-spaces around << when used like this: 10<<20, but # not otherwise (particularly, not when used as streams) @@ -2114,8 +1784,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): match = Search(r'[^\s<]<([^\s=<].*)', reduced_line) if (match and not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around <') + return # Look for > that is not surrounded by spaces. Similar to the # above, we only trigger if both sides are missing spaces to avoid @@ -2124,8 +1793,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): if (match and not FindPreviousMatchingAngleBracket(clean_lines, linenum, match.group(1))): - error(filename, linenum, 'whitespace/operators', 3, - 'Missing spaces around >') + return # We allow no-spaces around >> for almost anything. This is because # C++11 allows ">>" to close nested templates, which accounts for @@ -2163,78 +1831,33 @@ def CheckSpacing(filename, clean_lines, linenum, error): if not (match.group(3) == ';' and len(match.group(2)) == 1 + len(match.group(4)) or not match.group(2) and Search(r'\bfor\s*\(.*; \)', line)): - error(filename, linenum, 'whitespace/parens', 5, - 'Mismatching spaces inside () in %s' % match.group(1)) + return if len(match.group(2)) not in [0, 1]: - error(filename, linenum, 'whitespace/parens', 5, - 'Should have zero or one spaces inside ( and ) in %s' % - match.group(1)) - - # Next we will look for issues with function calls. - CheckSpacingForFunctionCall(filename, line, linenum, error) + return # Check whether everything inside expressions is aligned correctly if any(line.find(k) >= 0 for k in BRACES if k != '{'): - CheckExpressionAlignment(filename, clean_lines, linenum, error) + return # Except after an opening paren, or after another opening brace (in case of # an initializer list, for instance), you should have spaces before your # braces. And since you should never have braces at the beginning of a line, # this is an easy test. match = Match(r'^(.*[^ ({]){', line) - if match: - # Try a bit harder to check for brace initialization. This - # happens in one of the following forms: - # Constructor() : initializer_list_{} { ... } - # Constructor{}.MemberFunction() - # Type variable{}; - # FunctionCall(type{}, ...); - # LastArgument(..., type{}); - # LOG(INFO) << type{} << " ..."; - # map_of_type[{...}] = ...; - # - # We check for the character following the closing brace, and - # silence the warning if it's one of those listed above, i.e. - # "{.;,)<]". - # - # To account for nested initializer list, we allow any number of - # closing braces up to "{;,)<". We can't simply silence the - # warning on first sight of closing brace, because that would - # cause false negatives for things that are not initializer lists. - # Silence this: But not this: - # Outer{ if (...) { - # Inner{...} if (...){ // Missing space before { - # }; } - # - # There is a false negative with this approach if people inserted - # spurious semicolons, e.g. "if (cond){};", but we will catch the - # spurious semicolon with a separate check. - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - trailing_text = '' - if endpos > -1: - trailing_text = endline[endpos:] - for offset in range(endlinenum + 1, - min(endlinenum + 3, clean_lines.NumLines() - 1)): - trailing_text += clean_lines.elided[offset] # Make sure '} else {' has spaces. if Search(r'}else', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before else') + return # You shouldn't have spaces before your brackets, except maybe after # 'delete []' or 'new char * []'. if Search(r'\w\s+\[', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Extra space before [') + return if Search(r'\{(?!\})\S', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space after {') + return if Search(r'\S(?<!\{)\}', line): - error(filename, linenum, 'whitespace/braces', 5, - 'Missing space before }') + return cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line) match = Search(r'(?<!\bkvec_t)' @@ -2245,6 +1868,7 @@ def CheckSpacing(filename, clean_lines, linenum, error): r'(?<!\bkbtree_t)' r'(?<!\bkbitr_t)' r'(?<!\bPMap)' + r'(?<!\bSet)' r'(?<!\bArrayOf)' r'(?<!\bDictionaryOf)' r'(?<!\bDict)' @@ -2349,30 +1973,6 @@ def CheckStyle(filename, clean_lines, linenum, error): error: The function to call with any errors found. """ - # Don't use "elided" lines here, otherwise we can't check commented lines. - # Don't want to use "raw" either, because we don't want to check inside - # C++11 raw strings, - raw_lines = clean_lines.lines_without_raw_strings - line = raw_lines[linenum] - - # One or three blank spaces at the beginning of the line is weird; it's - # hard to reconcile that with 2-space indents. - # NOTE: here are the conditions rob pike used for his tests. Mine aren't - # as sophisticated, but it may be worth becoming so: - # RLENGTH==initial_spaces - # if(RLENGTH > 20) complain = 0; - # if(match($0, " +(error|private|public|protected):")) complain = 0; - # if(match(prev, "&& *$")) complain = 0; - # if(match(prev, "\\|\\| *$")) complain = 0; - # if(match(prev, "[\",=><] *$")) complain = 0; - # if(match($0, " <<")) complain = 0; - # if(match(prev, " +for \\(")) complain = 0; - # if(prevodd && match(prevprev, " +for \\(")) complain = 0; - initial_spaces = 0 - - while initial_spaces < len(line) and line[initial_spaces] == ' ': - initial_spaces += 1 - # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckSpacing(filename, clean_lines, linenum, error) @@ -2612,7 +2212,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): def ProcessLine(filename, clean_lines, line, - function_state, nesting_state, error, + nesting_state, error, extra_check_functions=[]): """Processes a single line in the file. @@ -2621,8 +2221,6 @@ def ProcessLine(filename, clean_lines, line, clean_lines : An array of strings, each representing a line of the file, with comments stripped. line : Number of line being processed. - function_state : A _FunctionState instance which counts function - lines, etc. nesting_state : A _NestingState instance which maintains information about the current stack of nested blocks being parsed. @@ -2639,7 +2237,6 @@ def ProcessLine(filename, clean_lines, line, nesting_state.Update(clean_lines, line) if nesting_state.stack and nesting_state.stack[-1].inline_asm != _NO_ASM: return - CheckForFunctionLengths(filename, clean_lines, line, function_state, error) CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error) CheckForOldStyleComments(filename, init_lines[line], line, error) CheckStyle(filename, clean_lines, line, error) @@ -2670,7 +2267,6 @@ def ProcessFileData(filename, file_extension, lines, error, lines = (['// marker so line numbers and indices both start at 1'] + lines + ['// marker so line numbers end in a known way']) - function_state = _FunctionState() nesting_state = _NestingState() ResetNolintSuppressions() @@ -2695,12 +2291,13 @@ def ProcessFileData(filename, file_extension, lines, error, if file_extension == 'h': CheckForHeaderGuard(filename, lines, error) + CheckIncludes(filename, lines, error) RemoveMultiLineComments(filename, lines, error) clean_lines = CleansedLines(lines, init_lines) for line in range(clean_lines.NumLines()): ProcessLine(filename, clean_lines, line, - function_state, nesting_state, error, + nesting_state, error, extra_check_functions) # We check here rather than inside ProcessLine so that we see raw @@ -2744,12 +2341,10 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): lines = codecs.open( filename, 'r', 'utf8', 'replace').read().split('\n') - carriage_return_found = False # Remove trailing '\r'. for linenum in range(len(lines)): if lines[linenum].endswith('\r'): lines[linenum] = lines[linenum].rstrip('\r') - carriage_return_found = True except OSError: sys.stderr.write( @@ -2768,13 +2363,6 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) - if carriage_return_found and os.linesep != '\r\n': - # Use 0 for linenum since outputting only one error for potentially - # several lines. - Error(filename, 0, 'whitespace/newline', 1, - 'One or more unexpected \\r (^M) found;' - 'better to use only a \\n') - def PrintUsage(message): """Prints a brief usage string and exits, optionally with an error message. @@ -2810,6 +2398,8 @@ def ParseArguments(args): Returns: The list of filenames to lint. """ + opts = [] + filenames = [] try: (opts, filenames) = getopt.getopt(args, '', ['help', 'output=', |