mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #1957 from elmart/clint-pep8
Merge PR #1957 'Make clint.py pep8-compliant'
This commit is contained in:
commit
0854c21af3
384
clint.py
384
clint.py
@ -204,7 +204,7 @@ _ERROR_CATEGORIES = [
|
||||
'whitespace/semicolon',
|
||||
'whitespace/tab',
|
||||
'whitespace/todo'
|
||||
]
|
||||
]
|
||||
|
||||
# The default state of the category filter. This is overrided by the --filter=
|
||||
# flag. By default all errors are on, so only add here categories that should be
|
||||
@ -233,7 +233,7 @@ _ALT_TOKEN_REPLACEMENT = {
|
||||
'xor_eq': '^=',
|
||||
'not': '!',
|
||||
'not_eq': '!='
|
||||
}
|
||||
}
|
||||
|
||||
# Compile regular expression that matches all the above keywords. The "[ =()]"
|
||||
# bit is meant to avoid matching these keywords outside of boolean expressions.
|
||||
@ -278,6 +278,7 @@ _line_length = 80
|
||||
# This is set by --extensions flag.
|
||||
_valid_extensions = set(['c', 'h'])
|
||||
|
||||
|
||||
def ParseNolintSuppressions(filename, raw_line, linenum, error):
|
||||
"""Updates the global list of error-suppressions.
|
||||
|
||||
@ -301,7 +302,8 @@ def ParseNolintSuppressions(filename, raw_line, linenum, error):
|
||||
if category.startswith('(') and category.endswith(')'):
|
||||
category = category[1:-1]
|
||||
if category in _ERROR_CATEGORIES:
|
||||
_error_suppressions.setdefault(category, set()).add(linenum)
|
||||
_error_suppressions.setdefault(
|
||||
category, set()).add(linenum)
|
||||
else:
|
||||
error(filename, linenum, 'readability/nolint', 5,
|
||||
'Unknown NOLINT error category: %s' % category)
|
||||
@ -327,6 +329,7 @@ def IsErrorSuppressedByNolint(category, linenum):
|
||||
return (linenum in _error_suppressions.get(category, set()) or
|
||||
linenum in _error_suppressions.get(None, set()))
|
||||
|
||||
|
||||
def Match(pattern, s):
|
||||
"""Matches the string with the pattern, caching the compiled regexp."""
|
||||
# The regexp compilation caching is inlined in both Match and Search for
|
||||
@ -345,6 +348,7 @@ def Search(pattern, s):
|
||||
|
||||
|
||||
class _IncludeState(dict):
|
||||
|
||||
"""Tracks line numbers for includes, and the order in which includes appear.
|
||||
|
||||
As a dict, an _IncludeState object serves as a mapping between include
|
||||
@ -434,6 +438,7 @@ class _IncludeState(dict):
|
||||
|
||||
|
||||
class _CppLintState(object):
|
||||
|
||||
"""Maintains module-wide state.."""
|
||||
|
||||
def __init__(self):
|
||||
@ -470,12 +475,14 @@ class _CppLintState(object):
|
||||
error message.
|
||||
|
||||
Args:
|
||||
filters: A string of comma-separated filters (eg "+whitespace/indent").
|
||||
filters: A string of comma-separated filters.
|
||||
E.g. "+whitespace/indent".
|
||||
Each filter should start with + or -; else we die.
|
||||
|
||||
Raises:
|
||||
ValueError: The comma-separated filters did not all start with '+' or '-'.
|
||||
E.g. "-,+whitespace,-whitespace/indent,whitespace/badfilter"
|
||||
ValueError: The comma-separated filters did not all start with
|
||||
'+' or '-'.
|
||||
E.g. "-,+whitespace,-whitespace/indent,whitespace/bad"
|
||||
"""
|
||||
# Default filters always have less priority than the flag ones.
|
||||
self.filters = _DEFAULT_FILTERS[:]
|
||||
@ -485,8 +492,8 @@ class _CppLintState(object):
|
||||
self.filters.append(clean_filt)
|
||||
for filt in self.filters:
|
||||
if not (filt.startswith('+') or filt.startswith('-')):
|
||||
raise ValueError('Every filter in --filters must start with + or -'
|
||||
' (%s does not)' % filt)
|
||||
raise ValueError('Every filter in --filters must start with '
|
||||
'+ or - (%s does not)' % filt)
|
||||
|
||||
def ResetErrorCounts(self):
|
||||
"""Sets the module's error statistic back to zero."""
|
||||
@ -557,6 +564,7 @@ def _SetFilters(filters):
|
||||
|
||||
|
||||
class _FunctionState(object):
|
||||
|
||||
"""Tracks current function name and the number of lines in its body."""
|
||||
|
||||
_NORMAL_TRIGGER = 250 # for --v=0, 500 for --v=1, etc.
|
||||
@ -597,7 +605,8 @@ class _FunctionState(object):
|
||||
trigger = base_trigger * 2**_VerboseLevel()
|
||||
|
||||
if self.lines_in_function > trigger:
|
||||
error_level = int(math.log(self.lines_in_function / base_trigger, 2))
|
||||
error_level = int(
|
||||
math.log(self.lines_in_function / base_trigger, 2))
|
||||
# 50 => 0, 100 => 1, 200 => 2, 400 => 3, 800 => 4, 1600 => 5, ...
|
||||
if error_level > 5:
|
||||
error_level = 5
|
||||
@ -613,6 +622,7 @@ class _FunctionState(object):
|
||||
|
||||
|
||||
class FileInfo:
|
||||
|
||||
"""Provides utility functions for filenames.
|
||||
|
||||
FileInfo provides easy access to the components of a file's path
|
||||
@ -661,7 +671,7 @@ class FileInfo:
|
||||
return (project,) + os.path.splitext(rest)
|
||||
|
||||
def BaseName(self):
|
||||
"""File base name - text after the final slash, before the final period."""
|
||||
"""File base name - text after the final slash, before final period."""
|
||||
return self.Split()[1]
|
||||
|
||||
def Extension(self):
|
||||
@ -670,7 +680,7 @@ class FileInfo:
|
||||
|
||||
|
||||
def _ShouldPrintError(category, confidence, linenum):
|
||||
"""If confidence >= verbose, category passes filter and is not suppressed."""
|
||||
"""If confidence >= verbose, category passes filter and isn't suppressed."""
|
||||
|
||||
# There are three ways we might decide not to print an error message:
|
||||
# a "NOLINT(category)" comment appears in the source,
|
||||
@ -807,8 +817,8 @@ def RemoveMultiLineComments(filename, lines, error):
|
||||
return
|
||||
lineix_end = FindNextMultiLineCommentEnd(lines, lineix_begin)
|
||||
if lineix_end >= len(lines):
|
||||
error(filename, lineix_begin + 1, 'readability/multiline_comment', 5,
|
||||
'Could not find end of multi-line comment')
|
||||
error(filename, lineix_begin + 1, 'readability/multiline_comment',
|
||||
5, 'Could not find end of multi-line comment')
|
||||
return
|
||||
RemoveMultiLineCommentsFromRange(lines, lineix_begin, lineix_end + 1)
|
||||
lineix = lineix_end + 1
|
||||
@ -831,6 +841,7 @@ def CleanseComments(line):
|
||||
|
||||
|
||||
class CleansedLines(object):
|
||||
|
||||
"""Holds 3 copies of all lines with different preprocessing applied to them.
|
||||
|
||||
1) elided member contains lines without strings and comments,
|
||||
@ -848,7 +859,8 @@ class CleansedLines(object):
|
||||
for linenum in range(len(self.lines_without_raw_strings)):
|
||||
self.lines.append(CleanseComments(
|
||||
self.lines_without_raw_strings[linenum]))
|
||||
elided = self._CollapseStrings(self.lines_without_raw_strings[linenum])
|
||||
elided = self._CollapseStrings(
|
||||
self.lines_without_raw_strings[linenum])
|
||||
self.elided.append(CleanseComments(elided))
|
||||
|
||||
def NumLines(self):
|
||||
@ -868,9 +880,9 @@ class CleansedLines(object):
|
||||
The line with collapsed strings.
|
||||
"""
|
||||
if not _RE_PATTERN_INCLUDE.match(elided):
|
||||
# Remove escaped characters first to make quote/single quote collapsing
|
||||
# basic. Things that look like escaped characters shouldn't occur
|
||||
# outside of strings and chars.
|
||||
# Remove escaped characters first to make quote/single quote
|
||||
# collapsing basic. Things that look like escaped characters
|
||||
# shouldn't occur outside of strings and chars.
|
||||
elided = _RE_PATTERN_CLEANSE_LINE_ESCAPES.sub('', elided)
|
||||
elided = _RE_PATTERN_CLEANSE_LINE_SINGLE_QUOTES.sub("''", elided)
|
||||
elided = _RE_PATTERN_CLEANSE_LINE_DOUBLE_QUOTES.sub('""', elided)
|
||||
@ -923,10 +935,14 @@ def CloseExpression(clean_lines, linenum, pos):
|
||||
startchar = line[pos]
|
||||
if startchar not in '({[<':
|
||||
return (line, clean_lines.NumLines(), -1)
|
||||
if startchar == '(': endchar = ')'
|
||||
if startchar == '[': endchar = ']'
|
||||
if startchar == '{': endchar = '}'
|
||||
if startchar == '<': endchar = '>'
|
||||
if startchar == '(':
|
||||
endchar = ')'
|
||||
if startchar == '[':
|
||||
endchar = ']'
|
||||
if startchar == '{':
|
||||
endchar = '}'
|
||||
if startchar == '<':
|
||||
endchar = '>'
|
||||
|
||||
# Check first line
|
||||
(end_pos, num_open) = FindEndOfExpressionInLine(
|
||||
@ -995,10 +1011,14 @@ def ReverseCloseExpression(clean_lines, linenum, pos):
|
||||
endchar = line[pos]
|
||||
if endchar not in ')}]>':
|
||||
return (line, 0, -1)
|
||||
if endchar == ')': startchar = '('
|
||||
if endchar == ']': startchar = '['
|
||||
if endchar == '}': startchar = '{'
|
||||
if endchar == '>': startchar = '<'
|
||||
if endchar == ')':
|
||||
startchar = '('
|
||||
if endchar == ']':
|
||||
startchar = '['
|
||||
if endchar == '}':
|
||||
startchar = '{'
|
||||
if endchar == '>':
|
||||
startchar = '<'
|
||||
|
||||
# Check last line
|
||||
(start_pos, num_open) = FindStartOfExpressionInLine(
|
||||
@ -1101,8 +1121,8 @@ def CheckForHeaderGuard(filename, lines, error):
|
||||
|
||||
if define != ifndef:
|
||||
error(filename, 0, 'build/header_guard', 5,
|
||||
'#ifndef and #define don\'t match, suggested CPP variable is: %s' %
|
||||
cppvar)
|
||||
'#ifndef and #define don\'t match, suggested CPP variable is: %s'
|
||||
% cppvar)
|
||||
return
|
||||
|
||||
if endif != ('#endif // %s' % cppvar):
|
||||
@ -1136,9 +1156,11 @@ def CheckForBadCharacters(filename, lines, error):
|
||||
for linenum, line in enumerate(lines):
|
||||
if u'\ufffd' in line:
|
||||
error(filename, linenum, 'readability/utf8', 5,
|
||||
'Line contains invalid UTF-8 (or Unicode replacement character).')
|
||||
'Line contains invalid UTF-8'
|
||||
' (or Unicode replacement character).')
|
||||
if '\0' in line:
|
||||
error(filename, linenum, 'readability/nul', 5, 'Line contains NUL byte.')
|
||||
error(filename, linenum, 'readability/nul',
|
||||
5, 'Line contains NUL byte.')
|
||||
|
||||
|
||||
def CheckForNewlineAtEOF(filename, lines, error):
|
||||
@ -1220,7 +1242,7 @@ threading_list = (
|
||||
('localtime_r(', 'os_localtime_r('),
|
||||
('strtok_r(', 'os_strtok_r('),
|
||||
('ttyname_r(', 'os_ttyname_r('),
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def CheckPosixThreading(filename, clean_lines, linenum, error):
|
||||
@ -1241,7 +1263,8 @@ def CheckPosixThreading(filename, clean_lines, linenum, error):
|
||||
line = clean_lines.elided[linenum]
|
||||
for single_thread_function, multithread_safe_function in threading_list:
|
||||
ix = line.find(single_thread_function)
|
||||
# Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
|
||||
# Comparisons made explicit for clarity -- pylint:
|
||||
# disable=g-explicit-bool-comparison
|
||||
if ix >= 0 and (ix == 0 or (not line[ix - 1].isalnum() and
|
||||
line[ix - 1] not in ('_', '.', '>'))):
|
||||
error(filename, linenum, 'runtime/threadsafe_fn', 2,
|
||||
@ -1258,6 +1281,7 @@ _RE_PATTERN_INVALID_INCREMENT = re.compile(
|
||||
|
||||
|
||||
class _BlockInfo(object):
|
||||
|
||||
"""Stores information about a generic block of code."""
|
||||
|
||||
def __init__(self, seen_open_brace):
|
||||
@ -1267,6 +1291,7 @@ class _BlockInfo(object):
|
||||
|
||||
|
||||
class _PreprocessorInfo(object):
|
||||
|
||||
"""Stores checkpoints of nesting stacks when #if/#else is seen."""
|
||||
|
||||
def __init__(self, stack_before_if):
|
||||
@ -1281,6 +1306,7 @@ class _PreprocessorInfo(object):
|
||||
|
||||
|
||||
class _NestingState(object):
|
||||
|
||||
"""Holds states related to parsing braces."""
|
||||
|
||||
def __init__(self):
|
||||
@ -1325,7 +1351,8 @@ class _NestingState(object):
|
||||
"""
|
||||
if Match(r'^\s*#\s*(if|ifdef|ifndef)\b', line):
|
||||
# Beginning of #if block, save the nesting stack here. The saved
|
||||
# stack will allow us to restore the parsing state in the #else case.
|
||||
# stack will allow us to restore the parsing state in the #else
|
||||
# case.
|
||||
self.pp_stack.append(_PreprocessorInfo(copy.deepcopy(self.stack)))
|
||||
elif Match(r'^\s*#\s*(else|elif)\b', line):
|
||||
# Beginning of #else block
|
||||
@ -1335,7 +1362,8 @@ class _NestingState(object):
|
||||
# whole nesting stack up to this point. This is what we
|
||||
# keep after the #endif.
|
||||
self.pp_stack[-1].seen_else = True
|
||||
self.pp_stack[-1].stack_before_else = copy.deepcopy(self.stack)
|
||||
self.pp_stack[-1].stack_before_else = copy.deepcopy(
|
||||
self.stack)
|
||||
|
||||
# Restore the stack to how it was before the #if
|
||||
self.stack = copy.deepcopy(self.pp_stack[-1].stack_before_if)
|
||||
@ -1387,8 +1415,8 @@ class _NestingState(object):
|
||||
# Enter assembly block
|
||||
inner_block.inline_asm = _INSIDE_ASM
|
||||
else:
|
||||
# Not entering assembly block. If previous line was _END_ASM,
|
||||
# we will now shift to _NO_ASM state.
|
||||
# Not entering assembly block. If previous line was
|
||||
# _END_ASM, we will now shift to _NO_ASM state.
|
||||
inner_block.inline_asm = _NO_ASM
|
||||
elif (inner_block.inline_asm == _INSIDE_ASM and
|
||||
inner_block.open_parentheses == 0):
|
||||
@ -1446,8 +1474,8 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum,
|
||||
- invalid inner-style forward declaration.
|
||||
- >? and <? operators, and their >?= and <?= cousins.
|
||||
|
||||
Additionally, check for constructor/destructor style violations and reference
|
||||
members, as it is very convenient to do so while checking for
|
||||
Additionally, check for constructor/destructor style violations and
|
||||
reference members, as it is very convenient to do so while checking for
|
||||
gcc-2 compliance.
|
||||
|
||||
Args:
|
||||
@ -1501,7 +1529,8 @@ def CheckForNonStandardConstructs(filename, clean_lines, linenum,
|
||||
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.')
|
||||
'>? and <? (max and min) operators are'
|
||||
' non-standard and deprecated.')
|
||||
|
||||
|
||||
def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
||||
@ -1525,7 +1554,8 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
||||
r'\bswitch\s*\((.*)\)\s*{'):
|
||||
match = Search(pattern, line)
|
||||
if match:
|
||||
fncall = match.group(1) # look inside the parens for function calls
|
||||
# look inside the parens for function calls
|
||||
fncall = match.group(1)
|
||||
break
|
||||
|
||||
# Except in if/for/while/switch, there should never be space
|
||||
@ -1547,7 +1577,8 @@ def CheckSpacingForFunctionCall(filename, line, linenum, error):
|
||||
not Search(r' \([^)]+\)\([^)]*(\)|,$)', fncall) and
|
||||
# Ignore pointers/references to arrays.
|
||||
not Search(r' \([^)]+\)\[[^\]]+\]', fncall)):
|
||||
if Search(r'\w\s*\(\s(?!\s*\\$)', fncall): # a ( used for a fn call
|
||||
# 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):
|
||||
@ -1629,7 +1660,8 @@ def CheckForFunctionLengths(filename, clean_lines, linenum,
|
||||
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
|
||||
# Declarations and trivial functions
|
||||
if Search(r'(;|})', start_line):
|
||||
body_found = True
|
||||
break # ... ignore
|
||||
elif Search(r'{', start_line):
|
||||
@ -1644,7 +1676,8 @@ def CheckForFunctionLengths(filename, clean_lines, linenum,
|
||||
function_state.Begin(function)
|
||||
break
|
||||
if not body_found:
|
||||
# No body for the function (or evidence of a non-function) was 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
|
||||
@ -1687,7 +1720,8 @@ def CheckComment(comment, filename, linenum, error):
|
||||
'"// TODO(my_username): Stuff."')
|
||||
|
||||
middle_whitespace = match.group(4)
|
||||
# Comparisons made explicit for correctness -- pylint: disable=g-explicit-bool-comparison
|
||||
# Comparisons made explicit for correctness -- pylint:
|
||||
# disable=g-explicit-bool-comparison
|
||||
if middle_whitespace != ' ' and middle_whitespace != '':
|
||||
error(filename, linenum, 'whitespace/todo', 2,
|
||||
'TODO(my_username): should be followed by a space')
|
||||
@ -1731,10 +1765,10 @@ def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix):
|
||||
# Found matching angle bracket
|
||||
return True
|
||||
elif operator == ',':
|
||||
# Got a comma after a bracket, this is most likely a template
|
||||
# argument. We have not seen a closing angle bracket yet, but
|
||||
# it's probably a few lines later if we look for it, so just
|
||||
# return early here.
|
||||
# Got a comma after a bracket, this is most likely a
|
||||
# template argument. We have not seen a closing angle
|
||||
# bracket yet, but it's probably a few lines later if we
|
||||
# look for it, so just return early here.
|
||||
return True
|
||||
else:
|
||||
# Got some other operator.
|
||||
@ -1746,7 +1780,8 @@ def FindNextMatchingAngleBracket(clean_lines, linenum, init_suffix):
|
||||
nesting_stack.append(operator)
|
||||
elif operator in (')', ']'):
|
||||
# We don't bother checking for matching () or []. If we got
|
||||
# something like (] or [), it would have been a syntax error.
|
||||
# something like (] or [), it would have been a syntax
|
||||
# error.
|
||||
nesting_stack.pop()
|
||||
|
||||
else:
|
||||
@ -1838,8 +1873,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
"""
|
||||
|
||||
# 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,
|
||||
# Don't want to use "raw" either, because we don't want to check inside
|
||||
# C++11 raw strings,
|
||||
raw = clean_lines.lines_without_raw_strings
|
||||
line = raw[linenum]
|
||||
|
||||
@ -1859,35 +1894,37 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
elided = clean_lines.elided
|
||||
prev_line = elided[linenum - 1]
|
||||
prevbrace = prev_line.rfind('{')
|
||||
# TODO(unknown): Don't complain if line before blank line, and line after,
|
||||
# both start with alnums and are indented the same amount.
|
||||
# This ignores whitespace at the start of a namespace block
|
||||
# because those are not usually indented.
|
||||
# TODO(unknown): Don't complain if line before blank line, and line
|
||||
# after,both start with alnums and are indented the same
|
||||
# amount. This ignores whitespace at the start of a
|
||||
# namespace block because those are not usually indented.
|
||||
if prevbrace != -1 and prev_line[prevbrace:].find('}') == -1:
|
||||
# OK, we have a blank line at the start of a code block. Before we
|
||||
# complain, we check if it is an exception to the rule: The previous
|
||||
# non-empty line has the parameters of a function header that are indented
|
||||
# 4 spaces (because they did not fit in a 80 column line when placed on
|
||||
# the same line as the function name). We also check for the case where
|
||||
# the previous line is indented 6 spaces, which may happen when the
|
||||
# initializers of a constructor do not fit into a 80 column line.
|
||||
# non-empty line has the parameters of a function header that are
|
||||
# indented 4 spaces (because they did not fit in a 80 column line
|
||||
# when placed on the same line as the function name). We also check
|
||||
# for the case where the previous line is indented 6 spaces, which
|
||||
# may happen when the initializers of a constructor do not fit into
|
||||
# a 80 column line.
|
||||
exception = False
|
||||
if Match(r' {6}\w', prev_line): # Initializer list?
|
||||
# We are looking for the opening column of initializer list, which
|
||||
# should be indented 4 spaces to cause 6 space indentation afterwards.
|
||||
search_position = linenum-2
|
||||
# We are looking for the opening column of initializer list,
|
||||
# which should be indented 4 spaces to cause 6 space indentation
|
||||
# afterwards.
|
||||
search_position = linenum - 2
|
||||
while (search_position >= 0
|
||||
and Match(r' {6}\w', elided[search_position])):
|
||||
search_position -= 1
|
||||
exception = (search_position >= 0
|
||||
and elided[search_position][:5] == ' :')
|
||||
else:
|
||||
# Search for the function arguments or an initializer list. We use a
|
||||
# simple heuristic here: If the line is indented 4 spaces; and we have a
|
||||
# closing paren, without the opening paren, followed by an opening brace
|
||||
# or colon (for initializer lists) we assume that it is the last line of
|
||||
# a function header. If we have a colon indented 4 spaces, it is an
|
||||
# initializer list.
|
||||
# Search for the function arguments or an initializer list. We
|
||||
# use a simple heuristic here: If the line is indented 4 spaces;
|
||||
# and we have a closing paren, without the opening paren,
|
||||
# followed by an opening brace or colon (for initializer lists)
|
||||
# we assume that it is the last line of a function header. If
|
||||
# we have a colon indented 4 spaces, it is an initializer list.
|
||||
exception = (Match(r' {4}\w[^\(]*\)\s*(const\s*)?(\{\s*$|:)',
|
||||
prev_line)
|
||||
or Match(r' {4}:', prev_line))
|
||||
@ -1917,15 +1954,16 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
commentpos = line.find('//')
|
||||
if commentpos != -1:
|
||||
# Check if the // may be in quotes. If so, ignore it
|
||||
# Comparisons made explicit for clarity -- pylint: disable=g-explicit-bool-comparison
|
||||
# Comparisons made explicit for clarity -- pylint:
|
||||
# disable=g-explicit-bool-comparison
|
||||
if (line.count('"', 0, commentpos) -
|
||||
line.count('\\"', 0, commentpos)) % 2 == 0: # not in quotes
|
||||
# Allow one space for new scopes, two spaces otherwise:
|
||||
if (not Match(r'^\s*{ //', line) and
|
||||
((commentpos >= 1 and
|
||||
line[commentpos-1] not in string.whitespace) or
|
||||
line[commentpos - 1] not in string.whitespace) or
|
||||
(commentpos >= 2 and
|
||||
line[commentpos-2] not in string.whitespace))):
|
||||
line[commentpos - 2] not in string.whitespace))):
|
||||
error(filename, linenum, 'whitespace/comments', 2,
|
||||
'At least two spaces is best between code and comments')
|
||||
# There should always be a space between the // and the comment
|
||||
@ -1994,8 +2032,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
# technically should should flag if at least one side is missing a
|
||||
# space. This is done to avoid some false positives with shifts.
|
||||
match = Search(r'[^\s<]<([^\s=<].*)', reduced_line)
|
||||
if (match and
|
||||
not FindNextMatchingAngleBracket(clean_lines, linenum, match.group(1))):
|
||||
if (match and not FindNextMatchingAngleBracket(clean_lines, linenum,
|
||||
match.group(1))):
|
||||
error(filename, linenum, 'whitespace/operators', 3,
|
||||
'Missing spaces around <')
|
||||
|
||||
@ -2058,7 +2096,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
'Should have zero or one spaces inside ( and ) in %s' %
|
||||
match.group(1))
|
||||
|
||||
# You should always have a space after a comma (either as fn arg or operator)
|
||||
# You should always have a space after a comma (either as fn arg or
|
||||
# operator).
|
||||
#
|
||||
# This does not apply when the non-space character following the
|
||||
# comma is another comma, since the only time when that happens is
|
||||
@ -2146,8 +2185,8 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error):
|
||||
'Semicolon defining empty statement. Use {} instead.')
|
||||
elif Search(r'^\s*;\s*$', line):
|
||||
error(filename, linenum, 'whitespace/semicolon', 5,
|
||||
'Line contains only semicolon. If this should be an empty statement, '
|
||||
'use {} instead.')
|
||||
'Line contains only semicolon. If this should be an empty'
|
||||
' statement, use {} instead.')
|
||||
elif (Search(r'\s+;\s*$', line) and
|
||||
not Search(r'\bfor\b', line)):
|
||||
error(filename, linenum, 'whitespace/semicolon', 5,
|
||||
@ -2192,18 +2231,20 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
||||
|
||||
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
|
||||
# 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')
|
||||
'{ should almost always be at the end'
|
||||
' of the previous line')
|
||||
|
||||
# An else clause should be on the same line as the preceding closing brace.
|
||||
if Match(r'\s*else\s*', line):
|
||||
@ -2220,13 +2261,17 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
||||
pos = line.find('else if')
|
||||
pos = line.find('(', pos)
|
||||
if pos > 0:
|
||||
(endline, _, endpos) = CloseExpression(clean_lines, linenum, pos)
|
||||
if endline[endpos:].find('{') == -1: # must be brace after if
|
||||
(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')
|
||||
'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')
|
||||
'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):
|
||||
@ -2326,7 +2371,8 @@ def CheckBraces(filename, clean_lines, linenum, error):
|
||||
# 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.
|
||||
# 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
|
||||
@ -2377,12 +2423,14 @@ def CheckEmptyBlockBody(filename, clean_lines, linenum, error):
|
||||
(end_line, end_linenum, end_pos) = CloseExpression(
|
||||
clean_lines, linenum, line.find('('))
|
||||
|
||||
# Output warning if what follows the condition expression is a semicolon.
|
||||
# No warning for all other cases, including whitespace or newline, since we
|
||||
# have a separate check for semicolons preceded by whitespace.
|
||||
# Output warning if what follows the condition expression is a
|
||||
# semicolon. No warning for all other cases, including whitespace or
|
||||
# newline, since we have a separate check for semicolons preceded by
|
||||
# whitespace.
|
||||
if end_pos >= 0 and Match(r';', end_line[end_pos:]):
|
||||
if matched.group(1) == 'if':
|
||||
error(filename, end_linenum, 'whitespace/empty_conditional_body', 5,
|
||||
error(filename, end_linenum,
|
||||
'whitespace/empty_conditional_body', 5,
|
||||
'Empty conditional bodies should use {}')
|
||||
else:
|
||||
error(filename, end_linenum, 'whitespace/empty_loop_body', 5,
|
||||
@ -2462,8 +2510,8 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
|
||||
"""
|
||||
|
||||
# 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,
|
||||
# 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]
|
||||
|
||||
@ -2474,7 +2522,8 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
|
||||
# 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
|
||||
# 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;
|
||||
@ -2490,7 +2539,8 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
|
||||
if line and line[-1].isspace():
|
||||
error(filename, linenum, 'whitespace/end_of_line', 4,
|
||||
'Line ends in whitespace. Consider deleting these extra spaces.')
|
||||
# There are certain situations we allow one space, notably for section labels
|
||||
# There are certain situations we allow one space, notably for section
|
||||
# labels
|
||||
elif ((initial_spaces == 1 or initial_spaces == 3) and
|
||||
not Match(r'\s*\w+\s*:\s*$', cleansed_line)):
|
||||
error(filename, linenum, 'whitespace/indent', 3,
|
||||
@ -2505,8 +2555,8 @@ def CheckStyle(filename, clean_lines, linenum, file_extension, nesting_state,
|
||||
line.startswith('#define %s' % cppvar) or
|
||||
line.startswith('#endif // %s' % cppvar)):
|
||||
is_header_guard = True
|
||||
# #include lines and header guards can be long, since there's no clean way to
|
||||
# split them.
|
||||
# #include lines and header guards can be long, since there's no clean way
|
||||
# to split them.
|
||||
#
|
||||
# URLs can be long too. It's possible to split these, but it makes them
|
||||
# harder to cut&paste.
|
||||
@ -2571,7 +2621,6 @@ def _ClassifyInclude(fileinfo, include, is_system):
|
||||
return _OTHER_HEADER
|
||||
|
||||
|
||||
|
||||
def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
||||
"""Check rules that are applicable to #include lines.
|
||||
|
||||
@ -2580,11 +2629,12 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
||||
applicable to #include lines in CheckLanguage must be put here.
|
||||
|
||||
Args:
|
||||
filename: The name of the current file.
|
||||
clean_lines: A CleansedLines instance containing the file.
|
||||
linenum: The number of the line to check.
|
||||
include_state: An _IncludeState instance in which the headers are inserted.
|
||||
error: The function to call with any errors found.
|
||||
filename : The name of the current file.
|
||||
clean_lines : A CleansedLines instance containing the file.
|
||||
linenum : The number of the line to check.
|
||||
include_state : An _IncludeState instance in which the headers are
|
||||
inserted.
|
||||
error : The function to call with any errors found.
|
||||
"""
|
||||
fileinfo = FileInfo(filename)
|
||||
|
||||
@ -2625,21 +2675,24 @@ def CheckIncludeLine(filename, clean_lines, linenum, include_state, error):
|
||||
_ClassifyInclude(fileinfo, include, is_system))
|
||||
if error_message:
|
||||
error(filename, linenum, 'build/include_order', 4,
|
||||
'%s. Should be: c system, c++ system, other.' % error_message)
|
||||
canonical_include = include_state.CanonicalizeAlphabeticalOrder(include)
|
||||
'%s. Should be: c system, c++ system, other.'
|
||||
% error_message)
|
||||
canonical_include = include_state.CanonicalizeAlphabeticalOrder(
|
||||
include)
|
||||
include_state.SetLastHeader(canonical_include)
|
||||
|
||||
|
||||
def _GetTextInside(text, start_pattern):
|
||||
r"""Retrieves all the text between matching open and close parentheses.
|
||||
|
||||
Given a string of lines and a regular expression string, retrieve all the text
|
||||
following the expression and between opening punctuation symbols like
|
||||
Given a string of lines and a regular expression string, retrieve all the
|
||||
text following the expression and between opening punctuation symbols like
|
||||
(, [, or {, and the matching close-punctuation symbol. This properly nested
|
||||
occurrences of the punctuations, so for the text like
|
||||
printf(a(), b(c()));
|
||||
a call to _GetTextInside(text, r'printf\(') will return 'a(), b(c())'.
|
||||
start_pattern must match string having an open punctuation symbol at the end.
|
||||
start_pattern must match string having an open punctuation symbol at the
|
||||
end.
|
||||
|
||||
Args:
|
||||
text: The lines to extract text. Its comments and strings must be elided.
|
||||
@ -2648,7 +2701,7 @@ def _GetTextInside(text, start_pattern):
|
||||
the text.
|
||||
Returns:
|
||||
The extracted text.
|
||||
None if either the opening string or ending punctuation could not be found.
|
||||
None if either the opening string or ending punctuation couldn't be found.
|
||||
"""
|
||||
# TODO(sugawarayu): Audit cpplint.py to see what places could be profitably
|
||||
# rewritten to use _GetTextInside (and use inferior regexp matching today).
|
||||
@ -2694,14 +2747,15 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
||||
uint32 inappropriately), but we do the best we can.
|
||||
|
||||
Args:
|
||||
filename: The name of the current file.
|
||||
clean_lines: A CleansedLines instance containing the file.
|
||||
linenum: The number of the line to check.
|
||||
file_extension: The extension (without the dot) of the filename.
|
||||
include_state: An _IncludeState instance in which the headers are inserted.
|
||||
nesting_state: A _NestingState instance which maintains information about
|
||||
the current stack of nested blocks being parsed.
|
||||
error: The function to call with any errors found.
|
||||
filename : The name of the current file.
|
||||
clean_lines : A CleansedLines instance containing the file.
|
||||
linenum : The number of the line to check.
|
||||
file_extension : The extension (without the dot) of the filename.
|
||||
include_state : An _IncludeState instance in which the headers are
|
||||
inserted.
|
||||
nesting_state : A _NestingState instance which maintains information
|
||||
about the current stack of nested blocks being parsed.
|
||||
error : The function to call with any errors found.
|
||||
"""
|
||||
# If the line is empty or consists of entirely a comment, no need to
|
||||
# check it.
|
||||
@ -2725,7 +2779,8 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
||||
match = Search(r'\b(short|long(?! +double)|long long)\b', line)
|
||||
if match:
|
||||
error(filename, linenum, 'runtime/int', 4,
|
||||
'Use int16_t/int64_t/etc, rather than the C type %s' % match.group(1))
|
||||
'Use int16_t/int64_t/etc, rather than the C type %s'
|
||||
% match.group(1))
|
||||
|
||||
# When snprintf is used, the second argument shouldn't be a literal.
|
||||
match = Search(r'snprintf\s*\(([^,]*),\s*([0-9]*)\s*,', line)
|
||||
@ -2789,20 +2844,29 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
||||
skip_next = False
|
||||
continue
|
||||
|
||||
if Search(r'sizeof\(.+\)', tok): continue
|
||||
if Search(r'arraysize\(\w+\)', tok): continue
|
||||
if Search(r'sizeof\(.+\)', tok):
|
||||
continue
|
||||
if Search(r'arraysize\(\w+\)', tok):
|
||||
continue
|
||||
|
||||
tok = tok.lstrip('(')
|
||||
tok = tok.rstrip(')')
|
||||
if not tok: continue
|
||||
if Match(r'\d+', tok): continue
|
||||
if Match(r'0[xX][0-9a-fA-F]+', tok): continue
|
||||
if Match(r'k[A-Z0-9]\w*', tok): continue
|
||||
if Match(r'(.+::)?k[A-Z0-9]\w*', tok): continue
|
||||
if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok): continue
|
||||
# A catch all for tricky sizeof cases, including 'sizeof expression',
|
||||
# 'sizeof(*type)', 'sizeof(const type)', 'sizeof(struct StructName)'
|
||||
# requires skipping the next token because we split on ' ' and '*'.
|
||||
if not tok:
|
||||
continue
|
||||
if Match(r'\d+', tok):
|
||||
continue
|
||||
if Match(r'0[xX][0-9a-fA-F]+', tok):
|
||||
continue
|
||||
if Match(r'k[A-Z0-9]\w*', tok):
|
||||
continue
|
||||
if Match(r'(.+::)?k[A-Z0-9]\w*', tok):
|
||||
continue
|
||||
if Match(r'(.+::)?[A-Z][A-Z0-9_]*', tok):
|
||||
continue
|
||||
# A catch all for tricky sizeof cases, including
|
||||
# 'sizeof expression', 'sizeof(*type)', 'sizeof(const type)',
|
||||
# 'sizeof(struct StructName)' requires skipping the next token
|
||||
# because we split on ' ' and '*'.
|
||||
if tok.startswith('sizeof'):
|
||||
skip_next = True
|
||||
continue
|
||||
@ -2810,8 +2874,9 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
||||
break
|
||||
if not is_const:
|
||||
error(filename, linenum, 'runtime/arrays', 1,
|
||||
'Do not use variable-length arrays. Use an appropriately named '
|
||||
"('k' followed by CamelCase) compile-time constant for the size.")
|
||||
"Do not use variable-length arrays. Use an appropriately"
|
||||
" named ('k' followed by CamelCase) compile-time constant for"
|
||||
" the size.")
|
||||
|
||||
# Detect TRUE and FALSE.
|
||||
match = Search(r'\b(TRUE|FALSE)\b', line)
|
||||
@ -2820,26 +2885,31 @@ def CheckLanguage(filename, clean_lines, linenum, file_extension,
|
||||
error(filename, linenum, 'readability/bool', 4,
|
||||
'Use %s instead of %s.' % (token.lower(), token))
|
||||
|
||||
|
||||
def ProcessLine(filename, file_extension, clean_lines, line,
|
||||
include_state, function_state, nesting_state, error,
|
||||
extra_check_functions=[]):
|
||||
"""Processes a single line in the file.
|
||||
|
||||
Args:
|
||||
filename: Filename of the file that is being processed.
|
||||
file_extension: The extension (dot not included) of the file.
|
||||
clean_lines: An array of strings, each representing a line of the file,
|
||||
with comments stripped.
|
||||
line: Number of line being processed.
|
||||
include_state: An _IncludeState instance in which the headers are inserted.
|
||||
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.
|
||||
error: A callable to which errors are reported, which takes 4 arguments:
|
||||
filename, line number, error level, and message
|
||||
extra_check_functions: An array of additional check functions that will be
|
||||
run on each source line. Each function takes 4
|
||||
arguments: filename, clean_lines, line, error
|
||||
filename : Filename of the file that is being processed.
|
||||
file_extension : The extension (dot not included) of the file.
|
||||
clean_lines : An array of strings, each representing a line of
|
||||
the file, with comments stripped.
|
||||
line : Number of line being processed.
|
||||
include_state : An _IncludeState instance in which the headers are
|
||||
inserted.
|
||||
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.
|
||||
error : A callable to which errors are reported, which
|
||||
takes 4 arguments: filename, line number, error
|
||||
level, and message
|
||||
extra_check_functions : An array of additional check functions that will
|
||||
be run on each source line. Each function takes 4
|
||||
arguments : filename, clean_lines, line, error
|
||||
"""
|
||||
raw_lines = clean_lines.raw_lines
|
||||
ParseNolintSuppressions(filename, raw_lines[line], line, error)
|
||||
@ -2848,7 +2918,8 @@ def ProcessLine(filename, file_extension, clean_lines, line,
|
||||
return
|
||||
CheckForFunctionLengths(filename, clean_lines, line, function_state, error)
|
||||
CheckForMultilineCommentsAndStrings(filename, clean_lines, line, error)
|
||||
CheckStyle(filename, clean_lines, line, file_extension, nesting_state, error)
|
||||
CheckStyle(
|
||||
filename, clean_lines, line, file_extension, nesting_state, error)
|
||||
CheckLanguage(filename, clean_lines, line, file_extension, include_state,
|
||||
nesting_state, error)
|
||||
CheckForNonStandardConstructs(filename, clean_lines, line,
|
||||
@ -2857,6 +2928,7 @@ def ProcessLine(filename, file_extension, clean_lines, line,
|
||||
for check_fn in extra_check_functions:
|
||||
check_fn(filename, clean_lines, line, error)
|
||||
|
||||
|
||||
def ProcessFileData(filename, file_extension, lines, error,
|
||||
extra_check_functions=[]):
|
||||
"""Performs lint checks and reports any errors to the given error function.
|
||||
@ -2897,6 +2969,7 @@ def ProcessFileData(filename, file_extension, lines, error,
|
||||
|
||||
CheckForNewlineAtEOF(filename, lines, error)
|
||||
|
||||
|
||||
def ProcessFile(filename, vlevel, extra_check_functions=[]):
|
||||
"""Does neovim-lint on a single file.
|
||||
|
||||
@ -2930,7 +3003,8 @@ def ProcessFile(filename, vlevel, extra_check_functions=[]):
|
||||
codecs.getwriter('utf8'),
|
||||
'replace').read().split('\n')
|
||||
else:
|
||||
lines = codecs.open(filename, 'r', 'utf8', 'replace').read().split('\n')
|
||||
lines = codecs.open(
|
||||
filename, 'r', 'utf8', 'replace').read().split('\n')
|
||||
|
||||
carriage_return_found = False
|
||||
# Remove trailing '\r'.
|
||||
@ -2999,7 +3073,9 @@ def ParseArguments(args):
|
||||
The list of filenames to lint.
|
||||
"""
|
||||
try:
|
||||
(opts, filenames) = getopt.getopt(args, '', ['help', 'output=', 'verbose=',
|
||||
(opts, filenames) = getopt.getopt(args, '', ['help',
|
||||
'output=',
|
||||
'verbose=',
|
||||
'counting=',
|
||||
'filter=',
|
||||
'root=',
|
||||
@ -3018,7 +3094,8 @@ def ParseArguments(args):
|
||||
PrintUsage(None)
|
||||
elif opt == '--output':
|
||||
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
|
||||
elif opt == '--verbose':
|
||||
verbosity = int(val)
|
||||
@ -3028,7 +3105,8 @@ def ParseArguments(args):
|
||||
PrintCategories()
|
||||
elif opt == '--counting':
|
||||
if val not in ('total', 'toplevel', 'detailed'):
|
||||
PrintUsage('Valid counting options are total, toplevel, and detailed')
|
||||
PrintUsage(
|
||||
'Valid counting options are total, toplevel, and detailed')
|
||||
counting_style = val
|
||||
elif opt == '--linelength':
|
||||
global _line_length
|
||||
@ -3067,3 +3145,7 @@ def main():
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
# Ignore "too complex" warnings when using pymode.
|
||||
# pylama:ignore=C901
|
||||
|
Loading…
Reference in New Issue
Block a user