diff options
Diffstat (limited to 'src/clint.py')
-rwxr-xr-x | src/clint.py | 288 |
1 files changed, 35 insertions, 253 deletions
diff --git a/src/clint.py b/src/clint.py index 28f6031a57..155f5f2ce5 100755 --- a/src/clint.py +++ b/src/clint.py @@ -1,5 +1,7 @@ #!/usr/bin/env python3 # +# https://github.com/cpplint/cpplint +# # Copyright (c) 2009 Google Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -29,15 +31,9 @@ """Lints C files in the Neovim source tree. -The goal of this script is to identify places in the code that *may* -be in non-compliance with Neovim style. It does not attempt to fix -up these problems -- the point is to educate. It does also not -attempt to find all problems, or to ensure that everything it does -find is legitimately a problem. - -In particular, we can get very confused by /* and // inside strings! -We do a small hack, which is to ignore //'s with "'s after them on the -same line, but it is far from perfect (in either direction). +This can get very confused by /* and // inside strings! We do a small hack, +which is to ignore //'s with "'s after them on the same line, but it is far +from perfect (in either direction). """ @@ -61,25 +57,10 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] <file> [file] ... The style guidelines this tries to follow are those in - http://neovim.io/develop/style-guide.xml - - Note: This is Google's cpplint.py modified for use with the Neovim project, - which follows the Google C++ coding convention except with the following - modifications: - - * Function names are lower_case. - * Struct and enum names that are not typedef-ed are struct lower_case and - enum lower_case. - * The opening brace for functions appear on the next line. - * All control structures must always use braces. - - Neovim is a C project. As a result, for .c and .h files, the following rules - are suppressed: + https://neovim.io/doc/user/dev_style.html#dev-style - * [whitespace/braces] { should almost always be at the end of the previous - line - * [build/include] Include the directory when naming .h files - * [runtime/int] Use int16_t/int64_t/etc, rather than the C type. + Note: This is Google's https://github.com/cpplint/cpplint modified for use + with the Neovim project. Every problem is given a confidence score from 1-5, with 5 meaning we are certain of the problem, and 1 meaning it could be a legitimate construct. @@ -176,7 +157,6 @@ _ERROR_CATEGORIES = [ 'build/printf_format', 'build/storage_class', 'readability/bool', - 'readability/braces', 'readability/multiline_comment', 'readability/multiline_string', 'readability/nul', @@ -202,7 +182,7 @@ _ERROR_CATEGORIES = [ 'whitespace/cast', ] -# The default state of the category filter. This is overrided by the --filter= +# The default state of the category filter. This is overridden by the --filter= # 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. @@ -1941,13 +1921,6 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): error(filename, linenum, 'whitespace/indent', 2, 'End of the inner expression should have ' 'the same indent as start') - else: - if (pos != depth_line_starts[depth][0] + 4 - and not (depth_line_starts[depth][1] == '{' - and pos == depth_line_starts[depth][0] + 2)): - if depth not in ignore_error_levels: - error(filename, linenum, 'whitespace/indent', 2, - 'Inner expression indentation should be 4') else: if (pos != level_starts[depth][0] + 1 + (level_starts[depth][2] == '{')): @@ -2331,213 +2304,36 @@ def CheckBraces(filename, clean_lines, linenum, error): prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] if (not Search(r'[,;:}{(]\s*$', prevline) and not Match(r'\s*#', prevline)): - error(filename, linenum, 'whitespace/braces', 4, - '{ should almost always be at the end' - ' of the previous line') + return # Brace must appear after function signature, but on the *next* line if Match(r'^(?:\w+(?: ?\*+)? )+\w+\(', line): pos = line.find('(') - (endline, end_linenum, endpos) = CloseExpression( - clean_lines, linenum, pos) + (endline, end_linenum, _) = CloseExpression(clean_lines, linenum, pos) if endline.endswith('{'): - error(filename, end_linenum, 'readability/braces', 5, - 'Brace starting function body must be placed on its own line') - else: - func_start_linenum = end_linenum + 1 - while not clean_lines.lines[func_start_linenum] == "{": - attrline = Match( - r'^((?!# *define).*?)' - r'(?:FUNC_ATTR|FUNC_API|REAL_FATTR)_\w+' - r'(?:\(\d+(, \d+)*\))?', - clean_lines.lines[func_start_linenum], - ) - if attrline: - if len(attrline.group(1)) != 2: - error(filename, func_start_linenum, - 'whitespace/indent', 5, - 'Function attribute line should have 2-space ' - 'indent') - - func_start_linenum += 1 - else: - func_start = clean_lines.lines[func_start_linenum] - if not func_start.startswith('enum ') and func_start.endswith('{'): - error(filename, func_start_linenum, - 'readability/braces', 5, - 'Brace starting function body must be placed ' - 'after the function signature') - break - - # An else clause should be on the same line as the preceding closing brace. - # If there is no preceding closing brace, there should be one. - if Match(r'\s*else\s*', line): - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if Match(r'\s*}\s*$', prevline): - error(filename, linenum, 'whitespace/newline', 4, - 'An else should appear on the same line as the preceding }') - else: - error(filename, linenum, 'readability/braces', 5, - 'An else should always have braces before it') - - # If should always have a brace - for blockstart in ('if', 'while', 'for'): - if Match(r'\s*{0}(?!\w)[^{{]*$'.format(blockstart), line): - pos = line.find(blockstart) - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression( - clean_lines, linenum, pos) - if endline[endpos:].find('{') == -1: - error(filename, linenum, 'readability/braces', 5, - '{} should always use braces'.format(blockstart)) - - # If braces come on one side of an else, they should be on both. - # However, we have to worry about "else if" that spans multiple lines! - if Search(r'}\s*else[^{]*$', line) or Match(r'[^}]*else\s*{', line): - if Search(r'}\s*else if([^{]*)$', line): # could be multi-line if - # find the ( after the if - pos = line.find('else if') - pos = line.find('(', pos) - if pos > 0: - (endline, _, endpos) = CloseExpression( - clean_lines, linenum, pos) - # must be brace after if - if endline[endpos:].find('{') == -1: - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side,' - ' it should have it on both') - else: # common case: else not followed by a multi-line if - error(filename, linenum, 'readability/braces', 5, - 'If an else has a brace on one side,' - ' it should have it on both') - - # Likewise, an else should never have the else clause on the same line - if Search(r'\belse [^\s{]', line) and not Search(r'\belse if\b', line): - error(filename, linenum, 'whitespace/newline', 4, - 'Else clause should never be on same line as else (use 2 lines)') - - # In the same way, a do/while should never be on one line - if Match(r'\s*do [^\s{]', line): - error(filename, linenum, 'whitespace/newline', 4, - 'do/while clauses should not be on a single line') - - # Block bodies should not be followed by a semicolon. Due to C++11 - # brace initialization, there are more places where semicolons are - # required than not, so we use a whitelist approach to check these - # rather than a blacklist. These are the places where "};" should - # be replaced by just "}": - # 1. Some flavor of block following closing parenthesis: - # for (;;) {}; - # while (...) {}; - # switch (...) {}; - # Function(...) {}; - # if (...) {}; - # if (...) else if (...) {}; - # - # 2. else block: - # if (...) else {}; - # - # 3. const member function: - # Function(...) const {}; - # - # 4. Block following some statement: - # x = 42; - # {}; - # - # 5. Block at the beginning of a function: - # Function(...) { - # {}; - # } - # - # Note that naively checking for the preceding "{" will also match - # braces inside multi-dimensional arrays, but this is fine since - # that expression will not contain semicolons. - # - # 6. Block following another block: - # while (true) {} - # {}; - # - # 7. End of namespaces: - # namespace {}; - # - # These semicolons seems far more common than other kinds of - # redundant semicolons, possibly due to people converting classes - # to namespaces. For now we do not warn for this case. - # - # Try matching case 1 first. - match = Match(r'^(.*\)\s*)\{', line) - if match: - # Matched closing parenthesis (case 1). Check the token before the - # matching opening parenthesis, and don't warn if it looks like a - # macro. This avoids these false positives: - # - macro that defines a base class - # - multi-line macro that defines a base class - # - macro that defines the whole class-head - # - # But we still issue warnings for macros that we know are safe to - # warn, specifically: - # - TEST, TEST_F, TEST_P, MATCHER, MATCHER_P - # - TYPED_TEST - # - INTERFACE_DEF - # - EXCLUSIVE_LOCKS_REQUIRED, SHARED_LOCKS_REQUIRED, LOCKS_EXCLUDED: - # - # We implement a whitelist of safe macros instead of a blacklist of - # unsafe macros, even though the latter appears less frequently in - # google code and would have been easier to implement. This is because - # the downside for getting the whitelist wrong means some extra - # semicolons, while the downside for getting the blacklist wrong - # would result in compile errors. - # - # In addition to macros, we also don't want to warn on compound - # literals. - closing_brace_pos = match.group(1).rfind(')') - opening_parenthesis = ReverseCloseExpression( - clean_lines, linenum, closing_brace_pos) - if opening_parenthesis[2] > -1: - line_prefix = opening_parenthesis[0][0:opening_parenthesis[2]] - macro = Search(r'\b([A-Z_]+)\s*$', line_prefix) - if ((macro and - macro.group(1) not in ( - 'TEST', 'TEST_F', 'MATCHER', 'MATCHER_P', 'TYPED_TEST', - 'EXCLUSIVE_LOCKS_REQUIRED', 'SHARED_LOCKS_REQUIRED', - 'LOCKS_EXCLUDED', 'INTERFACE_DEF')) or - Search(r'\s+=\s*$', line_prefix) or - Search(r'^\s*return\s*$', line_prefix)): - match = None - - else: - # Try matching cases 2-3. - match = Match(r'^(.*(?:else|\)\s*const)\s*)\{', line) - if not match: - # Try matching cases 4-6. These are always matched on separate - # lines. - # - # Note that we can't simply concatenate the previous line to the - # current line and do a single match, otherwise we may output - # duplicate warnings for the blank line case: - # if (cond) { - # // blank line - # } - prevline = GetPreviousNonBlankLine(clean_lines, linenum)[0] - if prevline and Search(r'[;{}]\s*$', prevline): - match = Match(r'^(\s*)\{', line) - - # Check matching closing brace - if match: - (endline, endlinenum, endpos) = CloseExpression( - clean_lines, linenum, len(match.group(1))) - if endpos > -1 and Match(r'^\s*;', endline[endpos:]): - # Current {} pair is eligible for semicolon check, and we have found - # the redundant semicolon, output warning here. - # - # Note: because we are scanning forward for opening braces, and - # outputting warnings for the matching closing brace, if there are - # nested blocks with trailing semicolons, we will get the error - # messages in reversed order. - error(filename, endlinenum, 'readability/braces', 4, - "You don't need a ; after a }") + return + func_start_linenum = end_linenum + 1 + while not clean_lines.lines[func_start_linenum] == "{": + attrline = Match( + r'^((?!# *define).*?)' + r'(?:FUNC_ATTR|FUNC_API|REAL_FATTR)_\w+' + r'(?:\(\d+(, \d+)*\))?', + clean_lines.lines[func_start_linenum], + ) + if attrline: + if len(attrline.group(1)) != 2: + error(filename, func_start_linenum, + 'whitespace/indent', 5, + 'Function attribute line should have 2-space ' + 'indent') + + func_start_linenum += 1 + else: + func_start = clean_lines.lines[func_start_linenum] + if not func_start.startswith('enum ') and func_start.endswith('{'): + return + break def CheckStyle(filename, clean_lines, linenum, error): """Checks rules from the 'C++ style rules' section of cppguide.html. @@ -2573,23 +2369,10 @@ def CheckStyle(filename, clean_lines, linenum, error): # if(match(prev, " +for \\(")) complain = 0; # if(prevodd && match(prevprev, " +for \\(")) complain = 0; initial_spaces = 0 - cleansed_line = clean_lines.elided[linenum] while initial_spaces < len(line) and line[initial_spaces] == ' ': initial_spaces += 1 - if (cleansed_line.count(';') > 1 and - # for loops are allowed two ;'s (and may run over two lines). - cleansed_line.find('for') == -1 and - (GetPreviousNonBlankLine(clean_lines, linenum)[0].find('for') == -1 or - GetPreviousNonBlankLine(clean_lines, linenum)[0].find(';') != -1) and - # It's ok to have many commands in a switch case that fits in 1 line - not ((cleansed_line.find('case ') != -1 or - cleansed_line.find('default:') != -1) and - cleansed_line.find('break;') != -1)): - error(filename, linenum, 'whitespace/newline', 0, - 'More than one command on the same line') - # Some more style checks CheckBraces(filename, clean_lines, linenum, error) CheckSpacing(filename, clean_lines, linenum, error) @@ -2707,7 +2490,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): if match: error(filename, linenum, 'runtime/printf', 4, 'Use xstrlcat or snprintf instead of %s' % match.group(1)) - if not Search(r'eval/typval\.[ch]$', filename): + if not Search(r'eval/typval\.[ch]$|eval/typval_defs\.h$', filename): match = Search(r'(?:\.|->)' r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?' r'|copylist|lock)' @@ -2720,8 +2503,7 @@ def CheckLanguage(filename, clean_lines, linenum, error): # Check for suspicious usage of "if" like # } if (a == b) { if Search(r'\}\s*if\s*\(', line): - error(filename, linenum, 'readability/braces', 4, - 'Did you mean "else if"? If not, start a new line for "if".') + return # Check for potential format string bugs like printf(foo). # We constrain the pattern not to pick things like DocidForPrintf(foo). |