|
10 | 10 |
|
11 | 11 | import collections
|
12 | 12 | import itertools
|
| 13 | +import tokenize |
13 | 14 |
|
14 | 15 | import astroid
|
15 | 16 | from astroid import decorators
|
@@ -93,6 +94,14 @@ class RefactoringChecker(checkers.BaseTokenChecker):
|
93 | 94 | 'following a chain of ifs, all of them containing a '
|
94 | 95 | 'return statement.'
|
95 | 96 | ),
|
| 97 | + 'R1707': ('Disallow trailing comma tuple', |
| 98 | + 'trailing-comma-tuple', |
| 99 | + 'In Python, a tuple is actually created by the comma symbol, ' |
| 100 | + 'not by the parentheses. Unfortunately, one can actually create a ' |
| 101 | + 'tuple by misplacing a trailing comma, which can lead to potential ' |
| 102 | + 'weird bugs in your code. You should always use parentheses ' |
| 103 | + 'explicitly for creating a tuple.', |
| 104 | + {'minversion': (3, 0)}), |
96 | 105 | }
|
97 | 106 | options = (('max-nested-blocks',
|
98 | 107 | {'default': 5, 'type': 'int', 'metavar': '<int>',
|
@@ -196,11 +205,32 @@ def _check_simplifiable_if(self, node):
|
196 | 205 |
|
197 | 206 | def process_tokens(self, tokens):
|
198 | 207 | # Process tokens and look for 'if' or 'elif'
|
199 |
| - for _, token, _, _, _ in tokens: |
200 |
| - if token == 'elif': |
| 208 | + for index, token in enumerate(tokens): |
| 209 | + if token.string == 'elif': |
201 | 210 | self._elifs.append(True)
|
202 |
| - elif token == 'if': |
| 211 | + elif token.string == 'if': |
203 | 212 | self._elifs.append(False)
|
| 213 | + elif six.PY3 and token.exact_type == tokenize.COMMA: |
| 214 | + self._check_one_element_trailing_comma_tuple(tokens, token, index) |
| 215 | + |
| 216 | + def _check_one_element_trailing_comma_tuple(self, tokens, token, index): |
| 217 | + left_tokens = itertools.islice(tokens, index + 1, None) |
| 218 | + same_line_tokens = ( |
| 219 | + other_token for other_token in left_tokens |
| 220 | + if other_token.start[0] == token.start[0] |
| 221 | + ) |
| 222 | + is_last_element = all( |
| 223 | + token.type in (tokenize.NEWLINE, tokenize.COMMENT) |
| 224 | + for token in same_line_tokens |
| 225 | + ) |
| 226 | + if not is_last_element: |
| 227 | + return |
| 228 | + |
| 229 | + assign_token = tokens[index-2:index-1] |
| 230 | + if assign_token and assign_token[0].string == '=': |
| 231 | + if self.linter.is_message_enabled('trailing-comma-tuple'): |
| 232 | + self.add_message('trailing-comma-tuple', |
| 233 | + line=token.start[0]) |
204 | 234 |
|
205 | 235 | def leave_module(self, _):
|
206 | 236 | self._init()
|
|
0 commit comments