diff options
Diffstat (limited to 'src/clint.py')
-rwxr-xr-x | src/clint.py | 167 |
1 files changed, 127 insertions, 40 deletions
diff --git a/src/clint.py b/src/clint.py index efc5f18378..34af5d15fd 100755 --- a/src/clint.py +++ b/src/clint.py @@ -49,6 +49,7 @@ from __future__ import unicode_literals import codecs import copy +import fileinput import getopt import math # for log import os @@ -65,7 +66,7 @@ _USAGE = """ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] [--counting=total|toplevel|detailed] [--root=subdir] [--linelength=digits] [--record-errors=file] - [--suppress-errors=file] + [--suppress-errors=file] [--stdin-filename=filename] <file> [file] ... The style guidelines this tries to follow are those in @@ -167,6 +168,9 @@ Syntax: clint.py [--verbose=#] [--output=vs7] [--filter=-x,+y,...] suppress-errors=file Errors listed in the given file will not be reported. + + stdin-filename=filename + Use specified filename when reading from stdin (file "-"). """ # We categorize each error message we print. Here are the categories. @@ -182,6 +186,7 @@ _ERROR_CATEGORIES = [ 'build/include_order', 'build/printf_format', 'build/storage_class', + 'build/useless_fattr', 'readability/alt_tokens', 'readability/bool', 'readability/braces', @@ -200,6 +205,7 @@ _ERROR_CATEGORIES = [ 'runtime/printf', 'runtime/printf_format', 'runtime/threadsafe_fn', + 'runtime/deprecated', 'syntax/parenthesis', 'whitespace/alignment', 'whitespace/blank_line', @@ -569,9 +575,10 @@ class _CppLintState(object): def PrintErrorCounts(self): """Print a summary of errors by category, and the total.""" for category, count in self.errors_by_category.items(): - sys.stderr.write('Category \'%s\' errors found: %d\n' % + sys.stdout.write('Category \'%s\' errors found: %d\n' % (category, count)) - sys.stderr.write('Total errors found: %d\n' % self.error_count) + if self.error_count: + sys.stdout.write('Total errors found: %d\n' % self.error_count) def SuppressErrorsFrom(self, fname): """Open file and read a list of suppressed errors from it""" @@ -818,13 +825,13 @@ def Error(filename, linenum, category, confidence, message): if _ShouldPrintError(category, confidence, linenum): _cpplint_state.IncrementErrorCount(category) if _cpplint_state.output_format == 'vs7': - sys.stderr.write('%s(%s): %s [%s] [%d]\n' % ( + sys.stdout.write('%s(%s): %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) elif _cpplint_state.output_format == 'eclipse': - sys.stderr.write('%s:%s: warning: %s [%s] [%d]\n' % ( + sys.stdout.write('%s:%s: warning: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) else: - sys.stderr.write('%s:%s: %s [%s] [%d]\n' % ( + sys.stdout.write('%s:%s: %s [%s] [%d]\n' % ( filename, linenum, message, category, confidence)) @@ -1224,6 +1231,10 @@ def CheckForHeaderGuard(filename, lines, error): 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 FileInfo(filename).RelativePath() in set(( + 'func_attr.h', + )): + return cppvar = GetHeaderGuardCPPVariable(filename) @@ -2117,8 +2128,10 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): + (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 {)') + ('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 @@ -2135,7 +2148,8 @@ def CheckExpressionAlignment(filename, clean_lines, linenum, error, startpos=0): 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) + level_starts[depth] = (pos, line_ended_with_opening, brace, + linenum) if line_ended_with_opening: depth_line_starts[depth] = (prev_line_start, brace) else: @@ -2268,11 +2282,14 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): # //!< Header comment # or they begin with multiple slashes followed by a space: # //////// Header comment + # or they are Vim {{{ fold markers match = (Search(r'[=/-]{4,}\s*$', line[commentend:]) or Search(r'^/$', line[commentend:]) or Search(r'^!< ', line[commentend:]) or Search(r'^/< ', line[commentend:]) or - Search(r'^/+ ', line[commentend:])) + Search(r'^/+ ', line[commentend:]) or + Search(r'^(?:\{{3}|\}{3})\d*(?: |$)', + line[commentend:])) if not match: error(filename, linenum, 'whitespace/comments', 4, 'Should have a space between // and comment') @@ -2516,6 +2533,13 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): cast_line = re.sub(r'^# *define +\w+\([^)]*\)', '', line) match = Search(r'(?<!\bkvec_t)' + r'(?<!\bkvec_withinit_t)' + r'(?<!\bklist_t)' + r'(?<!\bkliter_t)' + r'(?<!\bkhash_t)' + r'(?<!\bkbtree_t)' + r'(?<!\bkbitr_t)' + r'(?<!\bPMap)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) @@ -2560,22 +2584,51 @@ def CheckBraces(filename, clean_lines, linenum, error): line = clean_lines.elided[linenum] # get rid of comments and strings - if not (filename.endswith('.c') or filename.endswith('.h')): - if Match(r'\s*{\s*$', line): - # We allow an open brace to start a line in the case where someone - # is using braces in a block to explicitly create a new scope, which - # is commonly used to control the lifetime of stack-allocated - # variables. Braces are also used for brace initializers inside - # function calls. We don't detect this perfectly: we just don't - # complain if the last non-whitespace character on the previous - # non-blank line is ',', ';', ':', '(', '{', or '}', or if the - # previous line starts a preprocessor block. - 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') + if Match(r'\s+{\s*$', line): + # We allow an open brace to start a line in the case where someone + # is using braces in a block to explicitly create a new scope, which + # is commonly used to control the lifetime of stack-allocated + # variables. Braces are also used for brace initializers inside + # function calls. We don't detect this perfectly: we just don't + # complain if the last non-whitespace character on the previous + # non-blank line is ',', ';', ':', '(', '{', or '}', or if the + # previous line starts a preprocessor block. + 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') + + # 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) + 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).*?)(?:FUNC_ATTR|FUNC_API|REAL_FATTR)_\w+(?:\(\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. @@ -3001,9 +3054,10 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error): include = match.group(2) is_system = (match.group(1) == '<') if include in include_state: - error(filename, linenum, 'build/include', 4, - '"%s" already included at %s:%s' % - (include, filename, include_state[include])) + if is_system or not include.endswith('.c.h'): + error(filename, linenum, 'build/include', 4, + '"%s" already included at %s:%s' % + (include, filename, include_state[include])) else: include_state[include] = linenum @@ -3140,11 +3194,28 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, # Check if some verboten C functions are being used. if Search(r'\bsprintf\b', line): error(filename, linenum, 'runtime/printf', 5, - 'Never use sprintf. Use snprintf instead.') - match = Search(r'\b(strcpy|strcat)\b', line) + 'Use snprintf instead of sprintf.') + match = Search(r'\b(strncpy|STRNCPY)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Use xstrlcpy or snprintf instead of %s (unless this is from Vim)' + % match.group(1)) + match = Search(r'\b(strcpy)\b', line) + if match: + error(filename, linenum, 'runtime/printf', 4, + 'Use xstrlcpy or snprintf instead of %s' % match.group(1)) + match = Search(r'\b(STRNCAT|strncat|strcat|vim_strcat)\b', line) if match: error(filename, linenum, 'runtime/printf', 4, - 'Almost always, snprintf is better than %s' % match.group(1)) + 'Use xstrlcat or snprintf instead of %s' % match.group(1)) + if not Search(r'eval/typval\.[ch]$', filename): + match = Search(r'(?:\.|->)' + r'(?:lv_(?:first|last|refcount|len|watch|idx(?:_item)?' + r'|copylist|lock)' + r'|li_(?:next|prev|tv))\b', line) + if match: + error(filename, linenum, 'runtime/deprecated', 4, + 'Accessing list_T internals directly is prohibited') # Check for suspicious usage of "if" like # } if (a == b) { @@ -3232,6 +3303,13 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension, error(filename, linenum, 'readability/bool', 4, 'Use %s instead of %s.' % (token.lower(), token)) + # Detect MAYBE + match = Search(r'\b(MAYBE)\b', line) + if match: + token = match.group(1) + error(filename, linenum, 'readability/bool', 4, + 'Use kNONE from TriState instead of %s.' % token) + # Detect preincrement/predecrement match = Match(r'^\s*(?:\+\+|--)', line) if match: @@ -3382,10 +3460,12 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # is processed. if filename == '-': - lines = codecs.StreamReaderWriter(sys.stdin, - codecs.getreader('utf8'), - codecs.getwriter('utf8'), - 'replace').read().split('\n') + stdin = sys.stdin.read() + if sys.version_info < (3, 0): + stdin = stdin.decode('utf8') + lines = stdin.split('\n') + if _cpplint_state.stdin_filename is not None: + filename = _cpplint_state.stdin_filename else: lines = codecs.open( filename, 'r', 'utf8', 'replace').read().split('\n') @@ -3408,8 +3488,9 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]): # When reading from stdin, the extension is unknown, so no cpplint tests # should rely on the extension. if filename != '-' and file_extension not in _valid_extensions: - sys.stderr.write('Ignoring %s; not a valid file name ' - '(%s)\n' % (filename, ', '.join(_valid_extensions))) + sys.stderr.write('Ignoring {}; only linting {} files\n'.format( + filename, + ', '.join('.{}'.format(ext) for ext in _valid_extensions))) else: ProcessFileData(filename, file_extension, lines, Error, extra_check_functions) @@ -3465,7 +3546,9 @@ def ParseArguments(args): 'linelength=', 'extensions=', 'record-errors=', - 'suppress-errors=']) + 'suppress-errors=', + 'stdin-filename=', + ]) except getopt.GetoptError: PrintUsage('Invalid arguments.') @@ -3475,6 +3558,7 @@ def ParseArguments(args): counting_style = '' record_errors_file = None suppress_errors_file = None + stdin_filename = None for (opt, val) in opts: if opt == '--help': @@ -3511,6 +3595,8 @@ def ParseArguments(args): record_errors_file = val elif opt == '--suppress-errors': suppress_errors_file = val + elif opt == '--stdin-filename': + stdin_filename = val if not filenames: PrintUsage('No files were specified.') @@ -3521,6 +3607,7 @@ def ParseArguments(args): _SetCountingStyle(counting_style) _SuppressErrorsFrom(suppress_errors_file) _RecordErrorsTo(record_errors_file) + _cpplint_state.stdin_filename = stdin_filename return filenames @@ -3539,7 +3626,7 @@ def main(): if __name__ == '__main__': main() -# vim: ts=4 sts=4 sw=4 +# vim: ts=4 sts=4 sw=4 foldmarker=▶,▲ # Ignore "too complex" warnings when using pymode. # pylama:ignore=C901 |