Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/parso/python/errors.py: 22%

884 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:07 +0000

1# -*- coding: utf-8 -*- 

2import codecs 

3import warnings 

4import re 

5from contextlib import contextmanager 

6 

7from parso.normalizer import Normalizer, NormalizerConfig, Issue, Rule 

8from parso.python.tokenize import _get_token_collection 

9 

10_BLOCK_STMTS = ('if_stmt', 'while_stmt', 'for_stmt', 'try_stmt', 'with_stmt') 

11_STAR_EXPR_PARENTS = ('testlist_star_expr', 'testlist_comp', 'exprlist') 

12# This is the maximal block size given by python. 

13_MAX_BLOCK_SIZE = 20 

14_MAX_INDENT_COUNT = 100 

15ALLOWED_FUTURES = ( 

16 'nested_scopes', 'generators', 'division', 'absolute_import', 

17 'with_statement', 'print_function', 'unicode_literals', 'generator_stop', 

18) 

19_COMP_FOR_TYPES = ('comp_for', 'sync_comp_for') 

20 

21 

22def _get_rhs_name(node, version): 

23 type_ = node.type 

24 if type_ == "lambdef": 

25 return "lambda" 

26 elif type_ == "atom": 

27 comprehension = _get_comprehension_type(node) 

28 first, second = node.children[:2] 

29 if comprehension is not None: 

30 return comprehension 

31 elif second.type == "dictorsetmaker": 

32 if version < (3, 8): 

33 return "literal" 

34 else: 

35 if second.children[1] == ":" or second.children[0] == "**": 

36 return "dict display" 

37 else: 

38 return "set display" 

39 elif ( 

40 first == "(" 

41 and (second == ")" 

42 or (len(node.children) == 3 and node.children[1].type == "testlist_comp")) 

43 ): 

44 return "tuple" 

45 elif first == "(": 

46 return _get_rhs_name(_remove_parens(node), version=version) 

47 elif first == "[": 

48 return "list" 

49 elif first == "{" and second == "}": 

50 return "dict display" 

51 elif first == "{" and len(node.children) > 2: 

52 return "set display" 

53 elif type_ == "keyword": 

54 if "yield" in node.value: 

55 return "yield expression" 

56 if version < (3, 8): 

57 return "keyword" 

58 else: 

59 return str(node.value) 

60 elif type_ == "operator" and node.value == "...": 

61 return "Ellipsis" 

62 elif type_ == "comparison": 

63 return "comparison" 

64 elif type_ in ("string", "number", "strings"): 

65 return "literal" 

66 elif type_ == "yield_expr": 

67 return "yield expression" 

68 elif type_ == "test": 

69 return "conditional expression" 

70 elif type_ in ("atom_expr", "power"): 

71 if node.children[0] == "await": 

72 return "await expression" 

73 elif node.children[-1].type == "trailer": 

74 trailer = node.children[-1] 

75 if trailer.children[0] == "(": 

76 return "function call" 

77 elif trailer.children[0] == "[": 

78 return "subscript" 

79 elif trailer.children[0] == ".": 

80 return "attribute" 

81 elif ( 

82 ("expr" in type_ and "star_expr" not in type_) # is a substring 

83 or "_test" in type_ 

84 or type_ in ("term", "factor") 

85 ): 

86 return "operator" 

87 elif type_ == "star_expr": 

88 return "starred" 

89 elif type_ == "testlist_star_expr": 

90 return "tuple" 

91 elif type_ == "fstring": 

92 return "f-string expression" 

93 return type_ # shouldn't reach here 

94 

95 

96def _iter_stmts(scope): 

97 """ 

98 Iterates over all statements and splits up simple_stmt. 

99 """ 

100 for child in scope.children: 

101 if child.type == 'simple_stmt': 

102 for child2 in child.children: 

103 if child2.type == 'newline' or child2 == ';': 

104 continue 

105 yield child2 

106 else: 

107 yield child 

108 

109 

110def _get_comprehension_type(atom): 

111 first, second = atom.children[:2] 

112 if second.type == 'testlist_comp' and second.children[1].type in _COMP_FOR_TYPES: 

113 if first == '[': 

114 return 'list comprehension' 

115 else: 

116 return 'generator expression' 

117 elif second.type == 'dictorsetmaker' and second.children[-1].type in _COMP_FOR_TYPES: 

118 if second.children[1] == ':': 

119 return 'dict comprehension' 

120 else: 

121 return 'set comprehension' 

122 return None 

123 

124 

125def _is_future_import(import_from): 

126 # It looks like a __future__ import that is relative is still a future 

127 # import. That feels kind of odd, but whatever. 

128 # if import_from.level != 0: 

129 # return False 

130 from_names = import_from.get_from_names() 

131 return [n.value for n in from_names] == ['__future__'] 

132 

133 

134def _remove_parens(atom): 

135 """ 

136 Returns the inner part of an expression like `(foo)`. Also removes nested 

137 parens. 

138 """ 

139 try: 

140 children = atom.children 

141 except AttributeError: 

142 pass 

143 else: 

144 if len(children) == 3 and children[0] == '(': 

145 return _remove_parens(atom.children[1]) 

146 return atom 

147 

148 

149def _skip_parens_bottom_up(node): 

150 """ 

151 Returns an ancestor node of an expression, skipping all levels of parens 

152 bottom-up. 

153 """ 

154 while node.parent is not None: 

155 node = node.parent 

156 if node.type != 'atom' or node.children[0] != '(': 

157 return node 

158 return None 

159 

160 

161def _iter_params(parent_node): 

162 return (n for n in parent_node.children if n.type == 'param' or n.type == 'operator') 

163 

164 

165def _is_future_import_first(import_from): 

166 """ 

167 Checks if the import is the first statement of a file. 

168 """ 

169 found_docstring = False 

170 for stmt in _iter_stmts(import_from.get_root_node()): 

171 if stmt.type == 'string' and not found_docstring: 

172 continue 

173 found_docstring = True 

174 

175 if stmt == import_from: 

176 return True 

177 if stmt.type == 'import_from' and _is_future_import(stmt): 

178 continue 

179 return False 

180 

181 

182def _iter_definition_exprs_from_lists(exprlist): 

183 def check_expr(child): 

184 if child.type == 'atom': 

185 if child.children[0] == '(': 

186 testlist_comp = child.children[1] 

187 if testlist_comp.type == 'testlist_comp': 

188 yield from _iter_definition_exprs_from_lists(testlist_comp) 

189 return 

190 else: 

191 # It's a paren that doesn't do anything, like 1 + (1) 

192 yield from check_expr(testlist_comp) 

193 return 

194 elif child.children[0] == '[': 

195 yield testlist_comp 

196 return 

197 yield child 

198 

199 if exprlist.type in _STAR_EXPR_PARENTS: 

200 for child in exprlist.children[::2]: 

201 yield from check_expr(child) 

202 else: 

203 yield from check_expr(exprlist) 

204 

205 

206def _get_expr_stmt_definition_exprs(expr_stmt): 

207 exprs = [] 

208 for list_ in expr_stmt.children[:-2:2]: 

209 if list_.type in ('testlist_star_expr', 'testlist'): 

210 exprs += _iter_definition_exprs_from_lists(list_) 

211 else: 

212 exprs.append(list_) 

213 return exprs 

214 

215 

216def _get_for_stmt_definition_exprs(for_stmt): 

217 exprlist = for_stmt.children[1] 

218 return list(_iter_definition_exprs_from_lists(exprlist)) 

219 

220 

221def _is_argument_comprehension(argument): 

222 return argument.children[1].type in _COMP_FOR_TYPES 

223 

224 

225def _any_fstring_error(version, node): 

226 if version < (3, 9) or node is None: 

227 return False 

228 if node.type == "error_node": 

229 return any(child.type == "fstring_start" for child in node.children) 

230 elif node.type == "fstring": 

231 return True 

232 else: 

233 return node.search_ancestor("fstring") 

234 

235 

236class _Context: 

237 def __init__(self, node, add_syntax_error, parent_context=None): 

238 self.node = node 

239 self.blocks = [] 

240 self.parent_context = parent_context 

241 self._used_name_dict = {} 

242 self._global_names = [] 

243 self._local_params_names = [] 

244 self._nonlocal_names = [] 

245 self._nonlocal_names_in_subscopes = [] 

246 self._add_syntax_error = add_syntax_error 

247 

248 def is_async_funcdef(self): 

249 # Stupidly enough async funcdefs can have two different forms, 

250 # depending if a decorator is used or not. 

251 return self.is_function() \ 

252 and self.node.parent.type in ('async_funcdef', 'async_stmt') 

253 

254 def is_function(self): 

255 return self.node.type == 'funcdef' 

256 

257 def add_name(self, name): 

258 parent_type = name.parent.type 

259 if parent_type == 'trailer': 

260 # We are only interested in first level names. 

261 return 

262 

263 if parent_type == 'global_stmt': 

264 self._global_names.append(name) 

265 elif parent_type == 'nonlocal_stmt': 

266 self._nonlocal_names.append(name) 

267 elif parent_type == 'funcdef': 

268 self._local_params_names.extend( 

269 [param.name.value for param in name.parent.get_params()] 

270 ) 

271 else: 

272 self._used_name_dict.setdefault(name.value, []).append(name) 

273 

274 def finalize(self): 

275 """ 

276 Returns a list of nonlocal names that need to be part of that scope. 

277 """ 

278 self._analyze_names(self._global_names, 'global') 

279 self._analyze_names(self._nonlocal_names, 'nonlocal') 

280 

281 global_name_strs = {n.value: n for n in self._global_names} 

282 for nonlocal_name in self._nonlocal_names: 

283 try: 

284 global_name = global_name_strs[nonlocal_name.value] 

285 except KeyError: 

286 continue 

287 

288 message = "name '%s' is nonlocal and global" % global_name.value 

289 if global_name.start_pos < nonlocal_name.start_pos: 

290 error_name = global_name 

291 else: 

292 error_name = nonlocal_name 

293 self._add_syntax_error(error_name, message) 

294 

295 nonlocals_not_handled = [] 

296 for nonlocal_name in self._nonlocal_names_in_subscopes: 

297 search = nonlocal_name.value 

298 if search in self._local_params_names: 

299 continue 

300 if search in global_name_strs or self.parent_context is None: 

301 message = "no binding for nonlocal '%s' found" % nonlocal_name.value 

302 self._add_syntax_error(nonlocal_name, message) 

303 elif not self.is_function() or \ 

304 nonlocal_name.value not in self._used_name_dict: 

305 nonlocals_not_handled.append(nonlocal_name) 

306 return self._nonlocal_names + nonlocals_not_handled 

307 

308 def _analyze_names(self, globals_or_nonlocals, type_): 

309 def raise_(message): 

310 self._add_syntax_error(base_name, message % (base_name.value, type_)) 

311 

312 params = [] 

313 if self.node.type == 'funcdef': 

314 params = self.node.get_params() 

315 

316 for base_name in globals_or_nonlocals: 

317 found_global_or_nonlocal = False 

318 # Somehow Python does it the reversed way. 

319 for name in reversed(self._used_name_dict.get(base_name.value, [])): 

320 if name.start_pos > base_name.start_pos: 

321 # All following names don't have to be checked. 

322 found_global_or_nonlocal = True 

323 

324 parent = name.parent 

325 if parent.type == 'param' and parent.name == name: 

326 # Skip those here, these definitions belong to the next 

327 # scope. 

328 continue 

329 

330 if name.is_definition(): 

331 if parent.type == 'expr_stmt' \ 

332 and parent.children[1].type == 'annassign': 

333 if found_global_or_nonlocal: 

334 # If it's after the global the error seems to be 

335 # placed there. 

336 base_name = name 

337 raise_("annotated name '%s' can't be %s") 

338 break 

339 else: 

340 message = "name '%s' is assigned to before %s declaration" 

341 else: 

342 message = "name '%s' is used prior to %s declaration" 

343 

344 if not found_global_or_nonlocal: 

345 raise_(message) 

346 # Only add an error for the first occurence. 

347 break 

348 

349 for param in params: 

350 if param.name.value == base_name.value: 

351 raise_("name '%s' is parameter and %s"), 

352 

353 @contextmanager 

354 def add_block(self, node): 

355 self.blocks.append(node) 

356 yield 

357 self.blocks.pop() 

358 

359 def add_context(self, node): 

360 return _Context(node, self._add_syntax_error, parent_context=self) 

361 

362 def close_child_context(self, child_context): 

363 self._nonlocal_names_in_subscopes += child_context.finalize() 

364 

365 

366class ErrorFinder(Normalizer): 

367 """ 

368 Searches for errors in the syntax tree. 

369 """ 

370 def __init__(self, *args, **kwargs): 

371 super().__init__(*args, **kwargs) 

372 self._error_dict = {} 

373 self.version = self.grammar.version_info 

374 

375 def initialize(self, node): 

376 def create_context(node): 

377 if node is None: 

378 return None 

379 

380 parent_context = create_context(node.parent) 

381 if node.type in ('classdef', 'funcdef', 'file_input'): 

382 return _Context(node, self._add_syntax_error, parent_context) 

383 return parent_context 

384 

385 self.context = create_context(node) or _Context(node, self._add_syntax_error) 

386 self._indentation_count = 0 

387 

388 def visit(self, node): 

389 if node.type == 'error_node': 

390 with self.visit_node(node): 

391 # Don't need to investigate the inners of an error node. We 

392 # might find errors in there that should be ignored, because 

393 # the error node itself already shows that there's an issue. 

394 return '' 

395 return super().visit(node) 

396 

397 @contextmanager 

398 def visit_node(self, node): 

399 self._check_type_rules(node) 

400 

401 if node.type in _BLOCK_STMTS: 

402 with self.context.add_block(node): 

403 if len(self.context.blocks) == _MAX_BLOCK_SIZE: 

404 self._add_syntax_error(node, "too many statically nested blocks") 

405 yield 

406 return 

407 elif node.type == 'suite': 

408 self._indentation_count += 1 

409 if self._indentation_count == _MAX_INDENT_COUNT: 

410 self._add_indentation_error(node.children[1], "too many levels of indentation") 

411 

412 yield 

413 

414 if node.type == 'suite': 

415 self._indentation_count -= 1 

416 elif node.type in ('classdef', 'funcdef'): 

417 context = self.context 

418 self.context = context.parent_context 

419 self.context.close_child_context(context) 

420 

421 def visit_leaf(self, leaf): 

422 if leaf.type == 'error_leaf': 

423 if leaf.token_type in ('INDENT', 'ERROR_DEDENT'): 

424 # Indents/Dedents itself never have a prefix. They are just 

425 # "pseudo" tokens that get removed by the syntax tree later. 

426 # Therefore in case of an error we also have to check for this. 

427 spacing = list(leaf.get_next_leaf()._split_prefix())[-1] 

428 if leaf.token_type == 'INDENT': 

429 message = 'unexpected indent' 

430 else: 

431 message = 'unindent does not match any outer indentation level' 

432 self._add_indentation_error(spacing, message) 

433 else: 

434 if leaf.value.startswith('\\'): 

435 message = 'unexpected character after line continuation character' 

436 else: 

437 match = re.match('\\w{,2}("{1,3}|\'{1,3})', leaf.value) 

438 if match is None: 

439 message = 'invalid syntax' 

440 if ( 

441 self.version >= (3, 9) 

442 and leaf.value in _get_token_collection( 

443 self.version 

444 ).always_break_tokens 

445 ): 

446 message = "f-string: " + message 

447 else: 

448 if len(match.group(1)) == 1: 

449 message = 'EOL while scanning string literal' 

450 else: 

451 message = 'EOF while scanning triple-quoted string literal' 

452 self._add_syntax_error(leaf, message) 

453 return '' 

454 elif leaf.value == ':': 

455 parent = leaf.parent 

456 if parent.type in ('classdef', 'funcdef'): 

457 self.context = self.context.add_context(parent) 

458 

459 # The rest is rule based. 

460 return super().visit_leaf(leaf) 

461 

462 def _add_indentation_error(self, spacing, message): 

463 self.add_issue(spacing, 903, "IndentationError: " + message) 

464 

465 def _add_syntax_error(self, node, message): 

466 self.add_issue(node, 901, "SyntaxError: " + message) 

467 

468 def add_issue(self, node, code, message): 

469 # Overwrite the default behavior. 

470 # Check if the issues are on the same line. 

471 line = node.start_pos[0] 

472 args = (code, message, node) 

473 self._error_dict.setdefault(line, args) 

474 

475 def finalize(self): 

476 self.context.finalize() 

477 

478 for code, message, node in self._error_dict.values(): 

479 self.issues.append(Issue(node, code, message)) 

480 

481 

482class IndentationRule(Rule): 

483 code = 903 

484 

485 def _get_message(self, message, node): 

486 message = super()._get_message(message, node) 

487 return "IndentationError: " + message 

488 

489 

490@ErrorFinder.register_rule(type='error_node') 

491class _ExpectIndentedBlock(IndentationRule): 

492 message = 'expected an indented block' 

493 

494 def get_node(self, node): 

495 leaf = node.get_next_leaf() 

496 return list(leaf._split_prefix())[-1] 

497 

498 def is_issue(self, node): 

499 # This is the beginning of a suite that is not indented. 

500 return node.children[-1].type == 'newline' 

501 

502 

503class ErrorFinderConfig(NormalizerConfig): 

504 normalizer_class = ErrorFinder 

505 

506 

507class SyntaxRule(Rule): 

508 code = 901 

509 

510 def _get_message(self, message, node): 

511 message = super()._get_message(message, node) 

512 if ( 

513 "f-string" not in message 

514 and _any_fstring_error(self._normalizer.version, node) 

515 ): 

516 message = "f-string: " + message 

517 return "SyntaxError: " + message 

518 

519 

520@ErrorFinder.register_rule(type='error_node') 

521class _InvalidSyntaxRule(SyntaxRule): 

522 message = "invalid syntax" 

523 fstring_message = "f-string: invalid syntax" 

524 

525 def get_node(self, node): 

526 return node.get_next_leaf() 

527 

528 def is_issue(self, node): 

529 error = node.get_next_leaf().type != 'error_leaf' 

530 if ( 

531 error 

532 and _any_fstring_error(self._normalizer.version, node) 

533 ): 

534 self.add_issue(node, message=self.fstring_message) 

535 else: 

536 # Error leafs will be added later as an error. 

537 return error 

538 

539 

540@ErrorFinder.register_rule(value='await') 

541class _AwaitOutsideAsync(SyntaxRule): 

542 message = "'await' outside async function" 

543 

544 def is_issue(self, leaf): 

545 return not self._normalizer.context.is_async_funcdef() 

546 

547 def get_error_node(self, node): 

548 # Return the whole await statement. 

549 return node.parent 

550 

551 

552@ErrorFinder.register_rule(value='break') 

553class _BreakOutsideLoop(SyntaxRule): 

554 message = "'break' outside loop" 

555 

556 def is_issue(self, leaf): 

557 in_loop = False 

558 for block in self._normalizer.context.blocks: 

559 if block.type in ('for_stmt', 'while_stmt'): 

560 in_loop = True 

561 return not in_loop 

562 

563 

564@ErrorFinder.register_rule(value='continue') 

565class _ContinueChecks(SyntaxRule): 

566 message = "'continue' not properly in loop" 

567 message_in_finally = "'continue' not supported inside 'finally' clause" 

568 

569 def is_issue(self, leaf): 

570 in_loop = False 

571 for block in self._normalizer.context.blocks: 

572 if block.type in ('for_stmt', 'while_stmt'): 

573 in_loop = True 

574 if block.type == 'try_stmt': 

575 last_block = block.children[-3] 

576 if ( 

577 last_block == "finally" 

578 and leaf.start_pos > last_block.start_pos 

579 and self._normalizer.version < (3, 8) 

580 ): 

581 self.add_issue(leaf, message=self.message_in_finally) 

582 return False # Error already added 

583 if not in_loop: 

584 return True 

585 

586 

587@ErrorFinder.register_rule(value='from') 

588class _YieldFromCheck(SyntaxRule): 

589 message = "'yield from' inside async function" 

590 

591 def get_node(self, leaf): 

592 return leaf.parent.parent # This is the actual yield statement. 

593 

594 def is_issue(self, leaf): 

595 return leaf.parent.type == 'yield_arg' \ 

596 and self._normalizer.context.is_async_funcdef() 

597 

598 

599@ErrorFinder.register_rule(type='name') 

600class _NameChecks(SyntaxRule): 

601 message = 'cannot assign to __debug__' 

602 message_none = 'cannot assign to None' 

603 

604 def is_issue(self, leaf): 

605 self._normalizer.context.add_name(leaf) 

606 

607 if leaf.value == '__debug__' and leaf.is_definition(): 

608 return True 

609 

610 

611@ErrorFinder.register_rule(type='string') 

612class _StringChecks(SyntaxRule): 

613 message = "bytes can only contain ASCII literal characters." 

614 

615 def is_issue(self, leaf): 

616 string_prefix = leaf.string_prefix.lower() 

617 if 'b' in string_prefix \ 

618 and any(c for c in leaf.value if ord(c) > 127): 

619 # b'ä' 

620 return True 

621 

622 if 'r' not in string_prefix: 

623 # Raw strings don't need to be checked if they have proper 

624 # escaping. 

625 

626 payload = leaf._get_payload() 

627 if 'b' in string_prefix: 

628 payload = payload.encode('utf-8') 

629 func = codecs.escape_decode 

630 else: 

631 func = codecs.unicode_escape_decode 

632 

633 try: 

634 with warnings.catch_warnings(): 

635 # The warnings from parsing strings are not relevant. 

636 warnings.filterwarnings('ignore') 

637 func(payload) 

638 except UnicodeDecodeError as e: 

639 self.add_issue(leaf, message='(unicode error) ' + str(e)) 

640 except ValueError as e: 

641 self.add_issue(leaf, message='(value error) ' + str(e)) 

642 

643 

644@ErrorFinder.register_rule(value='*') 

645class _StarCheck(SyntaxRule): 

646 message = "named arguments must follow bare *" 

647 

648 def is_issue(self, leaf): 

649 params = leaf.parent 

650 if params.type == 'parameters' and params: 

651 after = params.children[params.children.index(leaf) + 1:] 

652 after = [child for child in after 

653 if child not in (',', ')') and not child.star_count] 

654 return len(after) == 0 

655 

656 

657@ErrorFinder.register_rule(value='**') 

658class _StarStarCheck(SyntaxRule): 

659 # e.g. {**{} for a in [1]} 

660 # TODO this should probably get a better end_pos including 

661 # the next sibling of leaf. 

662 message = "dict unpacking cannot be used in dict comprehension" 

663 

664 def is_issue(self, leaf): 

665 if leaf.parent.type == 'dictorsetmaker': 

666 comp_for = leaf.get_next_sibling().get_next_sibling() 

667 return comp_for is not None and comp_for.type in _COMP_FOR_TYPES 

668 

669 

670@ErrorFinder.register_rule(value='yield') 

671@ErrorFinder.register_rule(value='return') 

672class _ReturnAndYieldChecks(SyntaxRule): 

673 message = "'return' with value in async generator" 

674 message_async_yield = "'yield' inside async function" 

675 

676 def get_node(self, leaf): 

677 return leaf.parent 

678 

679 def is_issue(self, leaf): 

680 if self._normalizer.context.node.type != 'funcdef': 

681 self.add_issue(self.get_node(leaf), message="'%s' outside function" % leaf.value) 

682 elif self._normalizer.context.is_async_funcdef() \ 

683 and any(self._normalizer.context.node.iter_yield_exprs()): 

684 if leaf.value == 'return' and leaf.parent.type == 'return_stmt': 

685 return True 

686 

687 

688@ErrorFinder.register_rule(type='strings') 

689class _BytesAndStringMix(SyntaxRule): 

690 # e.g. 's' b'' 

691 message = "cannot mix bytes and nonbytes literals" 

692 

693 def _is_bytes_literal(self, string): 

694 if string.type == 'fstring': 

695 return False 

696 return 'b' in string.string_prefix.lower() 

697 

698 def is_issue(self, node): 

699 first = node.children[0] 

700 first_is_bytes = self._is_bytes_literal(first) 

701 for string in node.children[1:]: 

702 if first_is_bytes != self._is_bytes_literal(string): 

703 return True 

704 

705 

706@ErrorFinder.register_rule(type='import_as_names') 

707class _TrailingImportComma(SyntaxRule): 

708 # e.g. from foo import a, 

709 message = "trailing comma not allowed without surrounding parentheses" 

710 

711 def is_issue(self, node): 

712 if node.children[-1] == ',' and node.parent.children[-1] != ')': 

713 return True 

714 

715 

716@ErrorFinder.register_rule(type='import_from') 

717class _ImportStarInFunction(SyntaxRule): 

718 message = "import * only allowed at module level" 

719 

720 def is_issue(self, node): 

721 return node.is_star_import() and self._normalizer.context.parent_context is not None 

722 

723 

724@ErrorFinder.register_rule(type='import_from') 

725class _FutureImportRule(SyntaxRule): 

726 message = "from __future__ imports must occur at the beginning of the file" 

727 

728 def is_issue(self, node): 

729 if _is_future_import(node): 

730 if not _is_future_import_first(node): 

731 return True 

732 

733 for from_name, future_name in node.get_paths(): 

734 name = future_name.value 

735 allowed_futures = list(ALLOWED_FUTURES) 

736 if self._normalizer.version >= (3, 7): 

737 allowed_futures.append('annotations') 

738 if name == 'braces': 

739 self.add_issue(node, message="not a chance") 

740 elif name == 'barry_as_FLUFL': 

741 m = "Seriously I'm not implementing this :) ~ Dave" 

742 self.add_issue(node, message=m) 

743 elif name not in allowed_futures: 

744 message = "future feature %s is not defined" % name 

745 self.add_issue(node, message=message) 

746 

747 

748@ErrorFinder.register_rule(type='star_expr') 

749class _StarExprRule(SyntaxRule): 

750 message_iterable_unpacking = "iterable unpacking cannot be used in comprehension" 

751 

752 def is_issue(self, node): 

753 def check_delete_starred(node): 

754 while node.parent is not None: 

755 node = node.parent 

756 if node.type == 'del_stmt': 

757 return True 

758 if node.type not in (*_STAR_EXPR_PARENTS, 'atom'): 

759 return False 

760 return False 

761 

762 if self._normalizer.version >= (3, 9): 

763 ancestor = node.parent 

764 else: 

765 ancestor = _skip_parens_bottom_up(node) 

766 # starred expression not in tuple/list/set 

767 if ancestor.type not in (*_STAR_EXPR_PARENTS, 'dictorsetmaker') \ 

768 and not (ancestor.type == 'atom' and ancestor.children[0] != '('): 

769 self.add_issue(node, message="can't use starred expression here") 

770 return 

771 

772 if check_delete_starred(node): 

773 if self._normalizer.version >= (3, 9): 

774 self.add_issue(node, message="cannot delete starred") 

775 else: 

776 self.add_issue(node, message="can't use starred expression here") 

777 return 

778 

779 if node.parent.type == 'testlist_comp': 

780 # [*[] for a in [1]] 

781 if node.parent.children[1].type in _COMP_FOR_TYPES: 

782 self.add_issue(node, message=self.message_iterable_unpacking) 

783 

784 

785@ErrorFinder.register_rule(types=_STAR_EXPR_PARENTS) 

786class _StarExprParentRule(SyntaxRule): 

787 def is_issue(self, node): 

788 def is_definition(node, ancestor): 

789 if ancestor is None: 

790 return False 

791 

792 type_ = ancestor.type 

793 if type_ == 'trailer': 

794 return False 

795 

796 if type_ == 'expr_stmt': 

797 return node.start_pos < ancestor.children[-1].start_pos 

798 

799 return is_definition(node, ancestor.parent) 

800 

801 if is_definition(node, node.parent): 

802 args = [c for c in node.children if c != ','] 

803 starred = [c for c in args if c.type == 'star_expr'] 

804 if len(starred) > 1: 

805 if self._normalizer.version < (3, 9): 

806 message = "two starred expressions in assignment" 

807 else: 

808 message = "multiple starred expressions in assignment" 

809 self.add_issue(starred[1], message=message) 

810 elif starred: 

811 count = args.index(starred[0]) 

812 if count >= 256: 

813 message = "too many expressions in star-unpacking assignment" 

814 self.add_issue(starred[0], message=message) 

815 

816 

817@ErrorFinder.register_rule(type='annassign') 

818class _AnnotatorRule(SyntaxRule): 

819 # True: int 

820 # {}: float 

821 message = "illegal target for annotation" 

822 

823 def get_node(self, node): 

824 return node.parent 

825 

826 def is_issue(self, node): 

827 type_ = None 

828 lhs = node.parent.children[0] 

829 lhs = _remove_parens(lhs) 

830 try: 

831 children = lhs.children 

832 except AttributeError: 

833 pass 

834 else: 

835 if ',' in children or lhs.type == 'atom' and children[0] == '(': 

836 type_ = 'tuple' 

837 elif lhs.type == 'atom' and children[0] == '[': 

838 type_ = 'list' 

839 trailer = children[-1] 

840 

841 if type_ is None: 

842 if not (lhs.type == 'name' 

843 # subscript/attributes are allowed 

844 or lhs.type in ('atom_expr', 'power') 

845 and trailer.type == 'trailer' 

846 and trailer.children[0] != '('): 

847 return True 

848 else: 

849 # x, y: str 

850 message = "only single target (not %s) can be annotated" 

851 self.add_issue(lhs.parent, message=message % type_) 

852 

853 

854@ErrorFinder.register_rule(type='argument') 

855class _ArgumentRule(SyntaxRule): 

856 def is_issue(self, node): 

857 first = node.children[0] 

858 if self._normalizer.version < (3, 8): 

859 # a((b)=c) is valid in <3.8 

860 first = _remove_parens(first) 

861 if node.children[1] == '=' and first.type != 'name': 

862 if first.type == 'lambdef': 

863 # f(lambda: 1=1) 

864 if self._normalizer.version < (3, 8): 

865 message = "lambda cannot contain assignment" 

866 else: 

867 message = 'expression cannot contain assignment, perhaps you meant "=="?' 

868 else: 

869 # f(+x=1) 

870 if self._normalizer.version < (3, 8): 

871 message = "keyword can't be an expression" 

872 else: 

873 message = 'expression cannot contain assignment, perhaps you meant "=="?' 

874 self.add_issue(first, message=message) 

875 

876 if _is_argument_comprehension(node) and node.parent.type == 'classdef': 

877 self.add_issue(node, message='invalid syntax') 

878 

879 

880@ErrorFinder.register_rule(type='nonlocal_stmt') 

881class _NonlocalModuleLevelRule(SyntaxRule): 

882 message = "nonlocal declaration not allowed at module level" 

883 

884 def is_issue(self, node): 

885 return self._normalizer.context.parent_context is None 

886 

887 

888@ErrorFinder.register_rule(type='arglist') 

889class _ArglistRule(SyntaxRule): 

890 @property 

891 def message(self): 

892 if self._normalizer.version < (3, 7): 

893 return "Generator expression must be parenthesized if not sole argument" 

894 else: 

895 return "Generator expression must be parenthesized" 

896 

897 def is_issue(self, node): 

898 arg_set = set() 

899 kw_only = False 

900 kw_unpacking_only = False 

901 for argument in node.children: 

902 if argument == ',': 

903 continue 

904 

905 if argument.type == 'argument': 

906 first = argument.children[0] 

907 if _is_argument_comprehension(argument) and len(node.children) >= 2: 

908 # a(a, b for b in c) 

909 return True 

910 

911 if first in ('*', '**'): 

912 if first == '*': 

913 if kw_unpacking_only: 

914 # foo(**kwargs, *args) 

915 message = "iterable argument unpacking " \ 

916 "follows keyword argument unpacking" 

917 self.add_issue(argument, message=message) 

918 else: 

919 kw_unpacking_only = True 

920 else: # Is a keyword argument. 

921 kw_only = True 

922 if first.type == 'name': 

923 if first.value in arg_set: 

924 # f(x=1, x=2) 

925 message = "keyword argument repeated" 

926 if self._normalizer.version >= (3, 9): 

927 message += ": {}".format(first.value) 

928 self.add_issue(first, message=message) 

929 else: 

930 arg_set.add(first.value) 

931 else: 

932 if kw_unpacking_only: 

933 # f(**x, y) 

934 message = "positional argument follows keyword argument unpacking" 

935 self.add_issue(argument, message=message) 

936 elif kw_only: 

937 # f(x=2, y) 

938 message = "positional argument follows keyword argument" 

939 self.add_issue(argument, message=message) 

940 

941 

942@ErrorFinder.register_rule(type='parameters') 

943@ErrorFinder.register_rule(type='lambdef') 

944class _ParameterRule(SyntaxRule): 

945 # def f(x=3, y): pass 

946 message = "non-default argument follows default argument" 

947 

948 def is_issue(self, node): 

949 param_names = set() 

950 default_only = False 

951 star_seen = False 

952 for p in _iter_params(node): 

953 if p.type == 'operator': 

954 if p.value == '*': 

955 star_seen = True 

956 default_only = False 

957 continue 

958 

959 if p.name.value in param_names: 

960 message = "duplicate argument '%s' in function definition" 

961 self.add_issue(p.name, message=message % p.name.value) 

962 param_names.add(p.name.value) 

963 

964 if not star_seen: 

965 if p.default is None and not p.star_count: 

966 if default_only: 

967 return True 

968 elif p.star_count: 

969 star_seen = True 

970 default_only = False 

971 else: 

972 default_only = True 

973 

974 

975@ErrorFinder.register_rule(type='try_stmt') 

976class _TryStmtRule(SyntaxRule): 

977 message = "default 'except:' must be last" 

978 

979 def is_issue(self, try_stmt): 

980 default_except = None 

981 for except_clause in try_stmt.children[3::3]: 

982 if except_clause in ('else', 'finally'): 

983 break 

984 if except_clause == 'except': 

985 default_except = except_clause 

986 elif default_except is not None: 

987 self.add_issue(default_except, message=self.message) 

988 

989 

990@ErrorFinder.register_rule(type='fstring') 

991class _FStringRule(SyntaxRule): 

992 _fstring_grammar = None 

993 message_expr = "f-string expression part cannot include a backslash" 

994 message_nested = "f-string: expressions nested too deeply" 

995 message_conversion = "f-string: invalid conversion character: expected 's', 'r', or 'a'" 

996 

997 def _check_format_spec(self, format_spec, depth): 

998 self._check_fstring_contents(format_spec.children[1:], depth) 

999 

1000 def _check_fstring_expr(self, fstring_expr, depth): 

1001 if depth >= 2: 

1002 self.add_issue(fstring_expr, message=self.message_nested) 

1003 

1004 expr = fstring_expr.children[1] 

1005 if '\\' in expr.get_code(): 

1006 self.add_issue(expr, message=self.message_expr) 

1007 

1008 children_2 = fstring_expr.children[2] 

1009 if children_2.type == 'operator' and children_2.value == '=': 

1010 conversion = fstring_expr.children[3] 

1011 else: 

1012 conversion = children_2 

1013 if conversion.type == 'fstring_conversion': 

1014 name = conversion.children[1] 

1015 if name.value not in ('s', 'r', 'a'): 

1016 self.add_issue(name, message=self.message_conversion) 

1017 

1018 format_spec = fstring_expr.children[-2] 

1019 if format_spec.type == 'fstring_format_spec': 

1020 self._check_format_spec(format_spec, depth + 1) 

1021 

1022 def is_issue(self, fstring): 

1023 self._check_fstring_contents(fstring.children[1:-1]) 

1024 

1025 def _check_fstring_contents(self, children, depth=0): 

1026 for fstring_content in children: 

1027 if fstring_content.type == 'fstring_expr': 

1028 self._check_fstring_expr(fstring_content, depth) 

1029 

1030 

1031class _CheckAssignmentRule(SyntaxRule): 

1032 def _check_assignment(self, node, is_deletion=False, is_namedexpr=False, is_aug_assign=False): 

1033 error = None 

1034 type_ = node.type 

1035 if type_ == 'lambdef': 

1036 error = 'lambda' 

1037 elif type_ == 'atom': 

1038 first, second = node.children[:2] 

1039 error = _get_comprehension_type(node) 

1040 if error is None: 

1041 if second.type == 'dictorsetmaker': 

1042 if self._normalizer.version < (3, 8): 

1043 error = 'literal' 

1044 else: 

1045 if second.children[1] == ':': 

1046 error = 'dict display' 

1047 else: 

1048 error = 'set display' 

1049 elif first == "{" and second == "}": 

1050 if self._normalizer.version < (3, 8): 

1051 error = 'literal' 

1052 else: 

1053 error = "dict display" 

1054 elif first == "{" and len(node.children) > 2: 

1055 if self._normalizer.version < (3, 8): 

1056 error = 'literal' 

1057 else: 

1058 error = "set display" 

1059 elif first in ('(', '['): 

1060 if second.type == 'yield_expr': 

1061 error = 'yield expression' 

1062 elif second.type == 'testlist_comp': 

1063 # ([a, b] := [1, 2]) 

1064 # ((a, b) := [1, 2]) 

1065 if is_namedexpr: 

1066 if first == '(': 

1067 error = 'tuple' 

1068 elif first == '[': 

1069 error = 'list' 

1070 

1071 # This is not a comprehension, they were handled 

1072 # further above. 

1073 for child in second.children[::2]: 

1074 self._check_assignment(child, is_deletion, is_namedexpr, is_aug_assign) 

1075 else: # Everything handled, must be useless brackets. 

1076 self._check_assignment(second, is_deletion, is_namedexpr, is_aug_assign) 

1077 elif type_ == 'keyword': 

1078 if node.value == "yield": 

1079 error = "yield expression" 

1080 elif self._normalizer.version < (3, 8): 

1081 error = 'keyword' 

1082 else: 

1083 error = str(node.value) 

1084 elif type_ == 'operator': 

1085 if node.value == '...': 

1086 error = 'Ellipsis' 

1087 elif type_ == 'comparison': 

1088 error = 'comparison' 

1089 elif type_ in ('string', 'number', 'strings'): 

1090 error = 'literal' 

1091 elif type_ == 'yield_expr': 

1092 # This one seems to be a slightly different warning in Python. 

1093 message = 'assignment to yield expression not possible' 

1094 self.add_issue(node, message=message) 

1095 elif type_ == 'test': 

1096 error = 'conditional expression' 

1097 elif type_ in ('atom_expr', 'power'): 

1098 if node.children[0] == 'await': 

1099 error = 'await expression' 

1100 elif node.children[-2] == '**': 

1101 error = 'operator' 

1102 else: 

1103 # Has a trailer 

1104 trailer = node.children[-1] 

1105 assert trailer.type == 'trailer' 

1106 if trailer.children[0] == '(': 

1107 error = 'function call' 

1108 elif is_namedexpr and trailer.children[0] == '[': 

1109 error = 'subscript' 

1110 elif is_namedexpr and trailer.children[0] == '.': 

1111 error = 'attribute' 

1112 elif type_ == "fstring": 

1113 if self._normalizer.version < (3, 8): 

1114 error = 'literal' 

1115 else: 

1116 error = "f-string expression" 

1117 elif type_ in ('testlist_star_expr', 'exprlist', 'testlist'): 

1118 for child in node.children[::2]: 

1119 self._check_assignment(child, is_deletion, is_namedexpr, is_aug_assign) 

1120 elif ('expr' in type_ and type_ != 'star_expr' # is a substring 

1121 or '_test' in type_ 

1122 or type_ in ('term', 'factor')): 

1123 error = 'operator' 

1124 elif type_ == "star_expr": 

1125 if is_deletion: 

1126 if self._normalizer.version >= (3, 9): 

1127 error = "starred" 

1128 else: 

1129 self.add_issue(node, message="can't use starred expression here") 

1130 else: 

1131 if self._normalizer.version >= (3, 9): 

1132 ancestor = node.parent 

1133 else: 

1134 ancestor = _skip_parens_bottom_up(node) 

1135 if ancestor.type not in _STAR_EXPR_PARENTS and not is_aug_assign \ 

1136 and not (ancestor.type == 'atom' and ancestor.children[0] == '['): 

1137 message = "starred assignment target must be in a list or tuple" 

1138 self.add_issue(node, message=message) 

1139 

1140 self._check_assignment(node.children[1]) 

1141 

1142 if error is not None: 

1143 if is_namedexpr: 

1144 message = 'cannot use assignment expressions with %s' % error 

1145 else: 

1146 cannot = "can't" if self._normalizer.version < (3, 8) else "cannot" 

1147 message = ' '.join([cannot, "delete" if is_deletion else "assign to", error]) 

1148 self.add_issue(node, message=message) 

1149 

1150 

1151@ErrorFinder.register_rule(type='sync_comp_for') 

1152class _CompForRule(_CheckAssignmentRule): 

1153 message = "asynchronous comprehension outside of an asynchronous function" 

1154 

1155 def is_issue(self, node): 

1156 expr_list = node.children[1] 

1157 if expr_list.type != 'expr_list': # Already handled. 

1158 self._check_assignment(expr_list) 

1159 

1160 return node.parent.children[0] == 'async' \ 

1161 and not self._normalizer.context.is_async_funcdef() 

1162 

1163 

1164@ErrorFinder.register_rule(type='expr_stmt') 

1165class _ExprStmtRule(_CheckAssignmentRule): 

1166 message = "illegal expression for augmented assignment" 

1167 extended_message = "'{target}' is an " + message 

1168 

1169 def is_issue(self, node): 

1170 augassign = node.children[1] 

1171 is_aug_assign = augassign != '=' and augassign.type != 'annassign' 

1172 

1173 if self._normalizer.version <= (3, 8) or not is_aug_assign: 

1174 for before_equal in node.children[:-2:2]: 

1175 self._check_assignment(before_equal, is_aug_assign=is_aug_assign) 

1176 

1177 if is_aug_assign: 

1178 target = _remove_parens(node.children[0]) 

1179 # a, a[b], a.b 

1180 

1181 if target.type == "name" or ( 

1182 target.type in ("atom_expr", "power") 

1183 and target.children[1].type == "trailer" 

1184 and target.children[-1].children[0] != "(" 

1185 ): 

1186 return False 

1187 

1188 if self._normalizer.version <= (3, 8): 

1189 return True 

1190 else: 

1191 self.add_issue( 

1192 node, 

1193 message=self.extended_message.format( 

1194 target=_get_rhs_name(node.children[0], self._normalizer.version) 

1195 ), 

1196 ) 

1197 

1198 

1199@ErrorFinder.register_rule(type='with_item') 

1200class _WithItemRule(_CheckAssignmentRule): 

1201 def is_issue(self, with_item): 

1202 self._check_assignment(with_item.children[2]) 

1203 

1204 

1205@ErrorFinder.register_rule(type='del_stmt') 

1206class _DelStmtRule(_CheckAssignmentRule): 

1207 def is_issue(self, del_stmt): 

1208 child = del_stmt.children[1] 

1209 

1210 if child.type != 'expr_list': # Already handled. 

1211 self._check_assignment(child, is_deletion=True) 

1212 

1213 

1214@ErrorFinder.register_rule(type='expr_list') 

1215class _ExprListRule(_CheckAssignmentRule): 

1216 def is_issue(self, expr_list): 

1217 for expr in expr_list.children[::2]: 

1218 self._check_assignment(expr) 

1219 

1220 

1221@ErrorFinder.register_rule(type='for_stmt') 

1222class _ForStmtRule(_CheckAssignmentRule): 

1223 def is_issue(self, for_stmt): 

1224 # Some of the nodes here are already used, so no else if 

1225 expr_list = for_stmt.children[1] 

1226 if expr_list.type != 'expr_list': # Already handled. 

1227 self._check_assignment(expr_list) 

1228 

1229 

1230@ErrorFinder.register_rule(type='namedexpr_test') 

1231class _NamedExprRule(_CheckAssignmentRule): 

1232 # namedexpr_test: test [':=' test] 

1233 

1234 def is_issue(self, namedexpr_test): 

1235 # assigned name 

1236 first = namedexpr_test.children[0] 

1237 

1238 def search_namedexpr_in_comp_for(node): 

1239 while True: 

1240 parent = node.parent 

1241 if parent is None: 

1242 return parent 

1243 if parent.type == 'sync_comp_for' and parent.children[3] == node: 

1244 return parent 

1245 node = parent 

1246 

1247 if search_namedexpr_in_comp_for(namedexpr_test): 

1248 # [i+1 for i in (i := range(5))] 

1249 # [i+1 for i in (j := range(5))] 

1250 # [i+1 for i in (lambda: (j := range(5)))()] 

1251 message = 'assignment expression cannot be used in a comprehension iterable expression' 

1252 self.add_issue(namedexpr_test, message=message) 

1253 

1254 # defined names 

1255 exprlist = list() 

1256 

1257 def process_comp_for(comp_for): 

1258 if comp_for.type == 'sync_comp_for': 

1259 comp = comp_for 

1260 elif comp_for.type == 'comp_for': 

1261 comp = comp_for.children[1] 

1262 exprlist.extend(_get_for_stmt_definition_exprs(comp)) 

1263 

1264 def search_all_comp_ancestors(node): 

1265 has_ancestors = False 

1266 while True: 

1267 node = node.search_ancestor('testlist_comp', 'dictorsetmaker') 

1268 if node is None: 

1269 break 

1270 for child in node.children: 

1271 if child.type in _COMP_FOR_TYPES: 

1272 process_comp_for(child) 

1273 has_ancestors = True 

1274 break 

1275 return has_ancestors 

1276 

1277 # check assignment expressions in comprehensions 

1278 search_all = search_all_comp_ancestors(namedexpr_test) 

1279 if search_all: 

1280 if self._normalizer.context.node.type == 'classdef': 

1281 message = 'assignment expression within a comprehension ' \ 

1282 'cannot be used in a class body' 

1283 self.add_issue(namedexpr_test, message=message) 

1284 

1285 namelist = [expr.value for expr in exprlist if expr.type == 'name'] 

1286 if first.type == 'name' and first.value in namelist: 

1287 # [i := 0 for i, j in range(5)] 

1288 # [[(i := i) for j in range(5)] for i in range(5)] 

1289 # [i for i, j in range(5) if True or (i := 1)] 

1290 # [False and (i := 0) for i, j in range(5)] 

1291 message = 'assignment expression cannot rebind ' \ 

1292 'comprehension iteration variable %r' % first.value 

1293 self.add_issue(namedexpr_test, message=message) 

1294 

1295 self._check_assignment(first, is_namedexpr=True)