aboutsummaryrefslogtreecommitdiff
path: root/src/clint.py
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
committerJosh Rahm <joshuarahm@gmail.com>2023-01-25 18:31:31 +0000
commit9243becbedbb6a1592208051f8fa2b090dcc5e7d (patch)
tree607c2a862ec3f4399b8766383f6f8e04c4aa43b4 /src/clint.py
parent9e40b6e9e1bc67f2d856adb837ee64dd0e25b717 (diff)
parent3c48d3c83fc21dbc0841f9210f04bdb073d73cd1 (diff)
downloadrneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.gz
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.tar.bz2
rneovim-9243becbedbb6a1592208051f8fa2b090dcc5e7d.zip
Merge remote-tracking branch 'upstream/master' into usermarksusermarks
Diffstat (limited to 'src/clint.py')
-rwxr-xr-xsrc/clint.py288
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).