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

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

914 statements  

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

2import codecs 

3import sys 

4import warnings 

5import re 

6from contextlib import contextmanager 

7 

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

9from parso.python.tokenize import _get_token_collection 

10 

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

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

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

14_MAX_BLOCK_SIZE = 20 

15_MAX_INDENT_COUNT = 100 

16ALLOWED_FUTURES = ( 

17 'nested_scopes', 'generators', 'division', 'absolute_import', 

18 'with_statement', 'print_function', 'unicode_literals', 'generator_stop', 

19) 

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

21 

22 

23def _get_rhs_name(node, version): 

24 type_ = node.type 

25 if type_ == "lambdef": 

26 return "lambda" 

27 elif type_ == "atom": 

28 comprehension = _get_comprehension_type(node) 

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

30 if comprehension is not None: 

31 return comprehension 

32 elif second.type == "dictorsetmaker": 

33 if version < (3, 8): 

34 return "literal" 

35 else: 

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

37 if version < (3, 10): 

38 return "dict display" 

39 else: 

40 return "dict literal" 

41 else: 

42 return "set display" 

43 elif ( 

44 first == "(" 

45 and (second == ")" 

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

47 ): 

48 return "tuple" 

49 elif first == "(": 

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

51 elif first == "[": 

52 return "list" 

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

54 if version < (3, 10): 

55 return "dict display" 

56 else: 

57 return "dict literal" 

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

59 return "set display" 

60 elif type_ == "keyword": 

61 if "yield" in node.value: 

62 return "yield expression" 

63 if version < (3, 8): 

64 return "keyword" 

65 else: 

66 return str(node.value) 

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

68 if version < (3, 10): 

69 return "Ellipsis" 

70 else: 

71 return "ellipsis" 

72 elif type_ == "comparison": 

73 return "comparison" 

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

75 return "literal" 

76 elif type_ == "yield_expr": 

77 return "yield expression" 

78 elif type_ == "test": 

79 return "conditional expression" 

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

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

82 return "await expression" 

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

84 trailer = node.children[-1] 

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

86 return "function call" 

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

88 return "subscript" 

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

90 return "attribute" 

91 elif ( 

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

93 or "_test" in type_ 

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

95 ): 

96 if version < (3, 10): 

97 return "operator" 

98 else: 

99 return "expression" 

100 elif type_ == "star_expr": 

101 return "starred" 

102 elif type_ == "testlist_star_expr": 

103 return "tuple" 

104 elif type_ == "fstring": 

105 return "f-string expression" 

106 return type_ # shouldn't reach here 

107 

108 

109def _iter_stmts(scope): 

110 """ 

111 Iterates over all statements and splits up simple_stmt. 

112 """ 

113 for child in scope.children: 

114 if child.type == 'simple_stmt': 

115 for child2 in child.children: 

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

117 continue 

118 yield child2 

119 else: 

120 yield child 

121 

122 

123def _get_comprehension_type(atom): 

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

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

126 if first == '[': 

127 return 'list comprehension' 

128 else: 

129 return 'generator expression' 

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

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

132 return 'dict comprehension' 

133 else: 

134 return 'set comprehension' 

135 return None 

136 

137 

138def _is_future_import(import_from): 

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

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

141 # if import_from.level != 0: 

142 # return False 

143 from_names = import_from.get_from_names() 

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

145 

146 

147def _remove_parens(atom): 

148 """ 

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

150 parens. 

151 """ 

152 try: 

153 children = atom.children 

154 except AttributeError: 

155 pass 

156 else: 

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

158 return _remove_parens(atom.children[1]) 

159 return atom 

160 

161 

162def _skip_parens_bottom_up(node): 

163 """ 

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

165 bottom-up. 

166 """ 

167 while node.parent is not None: 

168 node = node.parent 

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

170 return node 

171 return None 

172 

173 

174def _iter_params(parent_node): 

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

176 

177 

178def _is_future_import_first(import_from): 

179 """ 

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

181 """ 

182 found_docstring = False 

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

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

185 continue 

186 found_docstring = True 

187 

188 if stmt == import_from: 

189 return True 

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

191 continue 

192 return False 

193 

194 

195def _iter_definition_exprs_from_lists(exprlist): 

196 def check_expr(child): 

197 if child.type == 'atom': 

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

199 testlist_comp = child.children[1] 

200 if testlist_comp.type == 'testlist_comp': 

201 yield from _iter_definition_exprs_from_lists(testlist_comp) 

202 return 

203 else: 

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

205 yield from check_expr(testlist_comp) 

206 return 

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

208 yield testlist_comp 

209 return 

210 yield child 

211 

212 if exprlist.type in _STAR_EXPR_PARENTS: 

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

214 yield from check_expr(child) 

215 else: 

216 yield from check_expr(exprlist) 

217 

218 

219def _get_expr_stmt_definition_exprs(expr_stmt): 

220 exprs = [] 

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

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

223 exprs += _iter_definition_exprs_from_lists(list_) 

224 else: 

225 exprs.append(list_) 

226 return exprs 

227 

228 

229def _get_for_stmt_definition_exprs(for_stmt): 

230 exprlist = for_stmt.children[1] 

231 return list(_iter_definition_exprs_from_lists(exprlist)) 

232 

233 

234def _is_argument_comprehension(argument): 

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

236 

237 

238def _any_fstring_error(version, node): 

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

240 return False 

241 if node.type == "error_node": 

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

243 elif node.type == "fstring": 

244 return True 

245 else: 

246 return node.search_ancestor("fstring") 

247 

248 

249class _Context: 

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

251 self.node = node 

252 self.blocks = [] 

253 self.parent_context = parent_context 

254 self._used_name_dict = {} 

255 self._global_names = [] 

256 self._local_params_names = [] 

257 self._nonlocal_names = [] 

258 self._nonlocal_names_in_subscopes = [] 

259 self._add_syntax_error = add_syntax_error 

260 

261 def is_async_funcdef(self): 

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

263 # depending if a decorator is used or not. 

264 return self.is_function() \ 

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

266 

267 def is_function(self): 

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

269 

270 def add_name(self, name): 

271 parent_type = name.parent.type 

272 if parent_type == 'trailer': 

273 # We are only interested in first level names. 

274 return 

275 

276 if parent_type == 'global_stmt': 

277 self._global_names.append(name) 

278 elif parent_type == 'nonlocal_stmt': 

279 self._nonlocal_names.append(name) 

280 elif parent_type == 'funcdef': 

281 self._local_params_names.extend( 

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

283 ) 

284 else: 

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

286 

287 def finalize(self): 

288 """ 

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

290 """ 

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

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

293 

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

295 for nonlocal_name in self._nonlocal_names: 

296 try: 

297 global_name = global_name_strs[nonlocal_name.value] 

298 except KeyError: 

299 continue 

300 

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

302 if global_name.start_pos < nonlocal_name.start_pos: 

303 error_name = global_name 

304 else: 

305 error_name = nonlocal_name 

306 self._add_syntax_error(error_name, message) 

307 

308 nonlocals_not_handled = [] 

309 for nonlocal_name in self._nonlocal_names_in_subscopes: 

310 search = nonlocal_name.value 

311 if search in self._local_params_names: 

312 continue 

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

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

315 self._add_syntax_error(nonlocal_name, message) 

316 elif not self.is_function() or \ 

317 nonlocal_name.value not in self._used_name_dict: 

318 nonlocals_not_handled.append(nonlocal_name) 

319 return self._nonlocal_names + nonlocals_not_handled 

320 

321 def _analyze_names(self, globals_or_nonlocals, type_): 

322 def raise_(message): 

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

324 

325 params = [] 

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

327 params = self.node.get_params() 

328 

329 for base_name in globals_or_nonlocals: 

330 found_global_or_nonlocal = False 

331 # Somehow Python does it the reversed way. 

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

333 if name.start_pos > base_name.start_pos: 

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

335 found_global_or_nonlocal = True 

336 

337 parent = name.parent 

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

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

340 # scope. 

341 continue 

342 

343 if name.is_definition(): 

344 if parent.type == 'expr_stmt' \ 

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

346 if found_global_or_nonlocal: 

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

348 # placed there. 

349 base_name = name 

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

351 break 

352 else: 

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

354 else: 

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

356 

357 if not found_global_or_nonlocal: 

358 raise_(message) 

359 # Only add an error for the first occurence. 

360 break 

361 

362 for param in params: 

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

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

365 

366 @contextmanager 

367 def add_block(self, node): 

368 self.blocks.append(node) 

369 yield 

370 self.blocks.pop() 

371 

372 def add_context(self, node): 

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

374 

375 def close_child_context(self, child_context): 

376 self._nonlocal_names_in_subscopes += child_context.finalize() 

377 

378 

379class ErrorFinder(Normalizer): 

380 """ 

381 Searches for errors in the syntax tree. 

382 """ 

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

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

385 self._error_dict = {} 

386 self.version = self.grammar.version_info 

387 

388 def initialize(self, node): 

389 def create_context(node): 

390 if node is None: 

391 return None 

392 

393 parent_context = create_context(node.parent) 

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

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

396 return parent_context 

397 

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

399 self._indentation_count = 0 

400 

401 def visit(self, node): 

402 if node.type == 'error_node': 

403 with self.visit_node(node): 

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

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

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

407 return '' 

408 return super().visit(node) 

409 

410 @contextmanager 

411 def visit_node(self, node): 

412 self._check_type_rules(node) 

413 

414 if node.type in _BLOCK_STMTS: 

415 with self.context.add_block(node): 

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

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

418 yield 

419 return 

420 elif node.type == 'suite': 

421 self._indentation_count += 1 

422 if self._indentation_count == _MAX_INDENT_COUNT: 

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

424 

425 yield 

426 

427 if node.type == 'suite': 

428 self._indentation_count -= 1 

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

430 context = self.context 

431 self.context = context.parent_context 

432 self.context.close_child_context(context) 

433 

434 def visit_leaf(self, leaf): 

435 if leaf.type == 'error_leaf': 

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

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

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

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

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

441 if leaf.token_type == 'INDENT': 

442 message = 'unexpected indent' 

443 else: 

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

445 self._add_indentation_error(spacing, message) 

446 else: 

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

448 message = 'unexpected character after line continuation character' 

449 else: 

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

451 if match is None: 

452 message = 'invalid syntax' 

453 if ( 

454 self.version >= (3, 9) 

455 and leaf.value in _get_token_collection( 

456 self.version 

457 ).always_break_tokens 

458 ): 

459 message = "f-string: " + message 

460 else: 

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

462 message = 'EOL while scanning string literal' 

463 else: 

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

465 self._add_syntax_error(leaf, message) 

466 return '' 

467 elif leaf.value == ':': 

468 parent = leaf.parent 

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

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

471 

472 # The rest is rule based. 

473 return super().visit_leaf(leaf) 

474 

475 def _add_indentation_error(self, spacing, message): 

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

477 

478 def _add_syntax_error(self, node, message): 

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

480 

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

482 # Overwrite the default behavior. 

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

484 line = node.start_pos[0] 

485 args = (code, message, node) 

486 self._error_dict.setdefault(line, args) 

487 

488 def finalize(self): 

489 self.context.finalize() 

490 

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

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

493 

494 

495class IndentationRule(Rule): 

496 code = 903 

497 

498 def _get_message(self, message, node): 

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

500 return "IndentationError: " + message 

501 

502 

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

504class _ExpectIndentedBlock(IndentationRule): 

505 message = 'expected an indented block' 

506 

507 def get_node(self, node): 

508 leaf = node.get_next_leaf() 

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

510 

511 def is_issue(self, node): 

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

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

514 

515 

516class ErrorFinderConfig(NormalizerConfig): 

517 normalizer_class = ErrorFinder 

518 

519 

520class SyntaxRule(Rule): 

521 code = 901 

522 

523 def _get_message(self, message, node): 

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

525 if ( 

526 "f-string" not in message 

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

528 ): 

529 message = "f-string: " + message 

530 return "SyntaxError: " + message 

531 

532 

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

534class _InvalidSyntaxRule(SyntaxRule): 

535 message = "invalid syntax" 

536 fstring_message = "f-string: invalid syntax" 

537 

538 def get_node(self, node): 

539 return node.get_next_leaf() 

540 

541 def is_issue(self, node): 

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

543 if ( 

544 error 

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

546 ): 

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

548 else: 

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

550 return error 

551 

552 

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

554class _AwaitOutsideAsync(SyntaxRule): 

555 message = "'await' outside async function" 

556 

557 def is_issue(self, leaf): 

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

559 

560 def get_error_node(self, node): 

561 # Return the whole await statement. 

562 return node.parent 

563 

564 

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

566class _BreakOutsideLoop(SyntaxRule): 

567 message = "'break' outside loop" 

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 return not in_loop 

575 

576 

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

578class _ContinueChecks(SyntaxRule): 

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

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

581 

582 def is_issue(self, leaf): 

583 in_loop = False 

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

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

586 in_loop = True 

587 if block.type == 'try_stmt': 

588 last_block = block.children[-3] 

589 if ( 

590 last_block == "finally" 

591 and leaf.start_pos > last_block.start_pos 

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

593 ): 

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

595 return False # Error already added 

596 if not in_loop: 

597 return True 

598 

599 

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

601class _YieldFromCheck(SyntaxRule): 

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

603 

604 def get_node(self, leaf): 

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

606 

607 def is_issue(self, leaf): 

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

609 and self._normalizer.context.is_async_funcdef() 

610 

611 

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

613class _NameChecks(SyntaxRule): 

614 message = 'cannot assign to __debug__' 

615 message_none = 'cannot assign to None' 

616 

617 def is_issue(self, leaf): 

618 self._normalizer.context.add_name(leaf) 

619 

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

621 return True 

622 

623 

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

625class _StringChecks(SyntaxRule): 

626 if sys.version_info < (3, 10): 

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

628 else: 

629 message = "bytes can only contain ASCII literal characters" 

630 

631 def is_issue(self, leaf): 

632 string_prefix = leaf.string_prefix.lower() 

633 if 'b' in string_prefix \ 

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

635 # b'ä' 

636 return True 

637 

638 if 'r' not in string_prefix: 

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

640 # escaping. 

641 

642 payload = leaf._get_payload() 

643 if 'b' in string_prefix: 

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

645 func = codecs.escape_decode 

646 else: 

647 func = codecs.unicode_escape_decode 

648 

649 try: 

650 with warnings.catch_warnings(): 

651 # The warnings from parsing strings are not relevant. 

652 warnings.filterwarnings('ignore') 

653 func(payload) 

654 except UnicodeDecodeError as e: 

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

656 except ValueError as e: 

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

658 

659 

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

661class _StarCheck(SyntaxRule): 

662 if sys.version_info[:2] < (3, 15): 

663 message = "named arguments must follow bare *" 

664 else: 

665 message = "named parameters must follow bare *" 

666 

667 def is_issue(self, leaf): 

668 params = leaf.parent 

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

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

671 after = [child for child in after 

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

673 return len(after) == 0 

674 

675 

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

677class _StarStarCheck(SyntaxRule): 

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

679 # TODO this should probably get a better end_pos including 

680 # the next sibling of leaf. 

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

682 

683 def is_issue(self, leaf): 

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

685 comp_for = leaf.get_next_sibling().get_next_sibling() 

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

687 

688 

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

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

691class _ReturnAndYieldChecks(SyntaxRule): 

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

693 message_async_yield = "'yield' inside async function" 

694 

695 def get_node(self, leaf): 

696 return leaf.parent 

697 

698 def is_issue(self, leaf): 

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

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

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

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

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

704 return True 

705 

706 

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

708class _BytesAndStringMix(SyntaxRule): 

709 # e.g. 's' b'' 

710 message = "cannot mix bytes and nonbytes literals" 

711 

712 def _is_bytes_literal(self, string): 

713 if string.type == 'fstring': 

714 return False 

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

716 

717 def is_issue(self, node): 

718 first = node.children[0] 

719 first_is_bytes = self._is_bytes_literal(first) 

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

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

722 return True 

723 

724 

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

726class _TrailingImportComma(SyntaxRule): 

727 # e.g. from foo import a, 

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

729 

730 def is_issue(self, node): 

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

732 return True 

733 

734 

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

736class _ImportStarInFunction(SyntaxRule): 

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

738 

739 def is_issue(self, node): 

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

741 

742 

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

744class _FutureImportRule(SyntaxRule): 

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

746 

747 def is_issue(self, node): 

748 if _is_future_import(node): 

749 if not _is_future_import_first(node): 

750 return True 

751 

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

753 name = future_name.value 

754 allowed_futures = list(ALLOWED_FUTURES) 

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

756 allowed_futures.append('annotations') 

757 if name == 'braces': 

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

759 elif name == 'barry_as_FLUFL': 

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

761 self.add_issue(node, message=m) 

762 elif name not in allowed_futures: 

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

764 self.add_issue(node, message=message) 

765 

766 

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

768class _StarExprRule(SyntaxRule): 

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

770 

771 def is_issue(self, node): 

772 def check_delete_starred(node): 

773 while node.parent is not None: 

774 node = node.parent 

775 if node.type == 'del_stmt': 

776 return True 

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

778 return False 

779 return False 

780 

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

782 ancestor = node.parent 

783 else: 

784 ancestor = _skip_parens_bottom_up(node) 

785 # starred expression not in tuple/list/set 

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

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

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

789 return 

790 

791 if check_delete_starred(node): 

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

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

794 else: 

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

796 return 

797 

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

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

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

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

802 

803 

804@ErrorFinder.register_rule(types=_STAR_EXPR_PARENTS) 

805class _StarExprParentRule(SyntaxRule): 

806 def is_issue(self, node): 

807 def is_definition(node, ancestor): 

808 if ancestor is None: 

809 return False 

810 

811 type_ = ancestor.type 

812 if type_ == 'trailer': 

813 return False 

814 

815 if type_ == 'expr_stmt': 

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

817 

818 return is_definition(node, ancestor.parent) 

819 

820 if is_definition(node, node.parent): 

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

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

823 if len(starred) > 1: 

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

825 message = "two starred expressions in assignment" 

826 else: 

827 message = "multiple starred expressions in assignment" 

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

829 elif starred: 

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

831 if count >= 256: 

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

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

834 

835 

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

837class _AnnotatorRule(SyntaxRule): 

838 # True: int 

839 # {}: float 

840 message = "illegal target for annotation" 

841 

842 def get_node(self, node): 

843 return node.parent 

844 

845 def is_issue(self, node): 

846 type_ = None 

847 lhs = node.parent.children[0] 

848 lhs = _remove_parens(lhs) 

849 try: 

850 children = lhs.children 

851 except AttributeError: 

852 pass 

853 else: 

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

855 type_ = 'tuple' 

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

857 type_ = 'list' 

858 trailer = children[-1] 

859 

860 if type_ is None: 

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

862 # subscript/attributes are allowed 

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

864 and trailer.type == 'trailer' 

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

866 return True 

867 else: 

868 # x, y: str 

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

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

871 

872 

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

874class _ArgumentRule(SyntaxRule): 

875 def is_issue(self, node): 

876 first = node.children[0] 

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

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

879 first = _remove_parens(first) 

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

881 if first.type == 'lambdef': 

882 # f(lambda: 1=1) 

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

884 message = "lambda cannot contain assignment" 

885 else: 

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

887 else: 

888 # f(+x=1) 

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

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

891 else: 

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

893 self.add_issue(first, message=message) 

894 

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

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

897 

898 

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

900class _NonlocalModuleLevelRule(SyntaxRule): 

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

902 

903 def is_issue(self, node): 

904 return self._normalizer.context.parent_context is None 

905 

906 

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

908class _ArglistRule(SyntaxRule): 

909 @property 

910 def message(self): 

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

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

913 else: 

914 return "Generator expression must be parenthesized" 

915 

916 def is_issue(self, node): 

917 arg_set = set() 

918 kw_only = False 

919 kw_unpacking_only = False 

920 for argument in node.children: 

921 if argument == ',': 

922 continue 

923 

924 if argument.type == 'argument': 

925 first = argument.children[0] 

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

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

928 return True 

929 

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

931 if first == '*': 

932 if kw_unpacking_only: 

933 # foo(**kwargs, *args) 

934 message = "iterable argument unpacking " \ 

935 "follows keyword argument unpacking" 

936 self.add_issue(argument, message=message) 

937 else: 

938 kw_unpacking_only = True 

939 elif argument.children[1] == ':=': 

940 # f(a := 1) is a positional argument, not a keyword one. 

941 pass 

942 else: # Is a keyword argument. 

943 kw_only = True 

944 if first.type == 'name': 

945 if first.value in arg_set: 

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

947 message = "keyword argument repeated" 

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

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

950 self.add_issue(first, message=message) 

951 else: 

952 arg_set.add(first.value) 

953 else: 

954 if kw_unpacking_only: 

955 # f(**x, y) 

956 message = "positional argument follows keyword argument unpacking" 

957 self.add_issue(argument, message=message) 

958 elif kw_only: 

959 # f(x=2, y) 

960 message = "positional argument follows keyword argument" 

961 self.add_issue(argument, message=message) 

962 

963 

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

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

966class _ParameterRule(SyntaxRule): 

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

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

969 

970 def is_issue(self, node): 

971 param_names = set() 

972 default_only = False 

973 star_seen = False 

974 for p in _iter_params(node): 

975 if p.type == 'operator': 

976 if p.value == '*': 

977 star_seen = True 

978 default_only = False 

979 continue 

980 

981 if p.name.value in param_names: 

982 if sys.version_info[:2] < (3, 15): 

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

984 else: 

985 message = "duplicate parameter '%s' in function definition" 

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

987 param_names.add(p.name.value) 

988 

989 if not star_seen: 

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

991 if default_only: 

992 return True 

993 elif p.star_count: 

994 star_seen = True 

995 default_only = False 

996 else: 

997 default_only = True 

998 

999 

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

1001class _TryStmtRule(SyntaxRule): 

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

1003 

1004 def is_issue(self, try_stmt): 

1005 default_except = None 

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

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

1008 break 

1009 if except_clause == 'except': 

1010 default_except = except_clause 

1011 elif default_except is not None: 

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

1013 

1014 

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

1016class _FStringRule(SyntaxRule): 

1017 _fstring_grammar = None 

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

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

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

1021 

1022 def _check_format_spec(self, format_spec, depth): 

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

1024 

1025 def _check_fstring_expr(self, fstring_expr, depth): 

1026 if depth >= 2: 

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

1028 

1029 expr = fstring_expr.children[1] 

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

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

1032 

1033 children_2 = fstring_expr.children[2] 

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

1035 conversion = fstring_expr.children[3] 

1036 else: 

1037 conversion = children_2 

1038 if conversion.type == 'fstring_conversion': 

1039 name = conversion.children[1] 

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

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

1042 

1043 format_spec = fstring_expr.children[-2] 

1044 if format_spec.type == 'fstring_format_spec': 

1045 self._check_format_spec(format_spec, depth + 1) 

1046 

1047 def is_issue(self, fstring): 

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

1049 

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

1051 for fstring_content in children: 

1052 if fstring_content.type == 'fstring_expr': 

1053 self._check_fstring_expr(fstring_content, depth) 

1054 

1055 

1056class _CheckAssignmentRule(SyntaxRule): 

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

1058 error = None 

1059 type_ = node.type 

1060 if type_ == 'lambdef': 

1061 error = 'lambda' 

1062 elif type_ == 'atom': 

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

1064 error = _get_comprehension_type(node) 

1065 if error is None: 

1066 if second.type == 'dictorsetmaker': 

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

1068 error = 'literal' 

1069 else: 

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

1071 if self._normalizer.version < (3, 10): 

1072 error = 'dict display' 

1073 else: 

1074 error = 'dict literal' 

1075 else: 

1076 error = 'set display' 

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

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

1079 error = 'literal' 

1080 else: 

1081 if self._normalizer.version < (3, 10): 

1082 error = "dict display" 

1083 else: 

1084 error = "dict literal" 

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

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

1087 error = 'literal' 

1088 else: 

1089 error = "set display" 

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

1091 if second.type == 'yield_expr': 

1092 error = 'yield expression' 

1093 elif second.type == 'testlist_comp': 

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

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

1096 if is_namedexpr: 

1097 if first == '(': 

1098 error = 'tuple' 

1099 elif first == '[': 

1100 error = 'list' 

1101 

1102 # This is not a comprehension, they were handled 

1103 # further above. 

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

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

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

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

1108 elif type_ == 'keyword': 

1109 if node.value == "yield": 

1110 error = "yield expression" 

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

1112 error = 'keyword' 

1113 else: 

1114 error = str(node.value) 

1115 elif type_ == 'operator': 

1116 if node.value == '...': 

1117 if self._normalizer.version < (3, 10): 

1118 error = 'Ellipsis' 

1119 else: 

1120 error = 'ellipsis' 

1121 elif type_ == 'comparison': 

1122 error = 'comparison' 

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

1124 error = 'literal' 

1125 elif type_ == 'yield_expr': 

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

1127 message = 'assignment to yield expression not possible' 

1128 self.add_issue(node, message=message) 

1129 elif type_ == 'test': 

1130 error = 'conditional expression' 

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

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

1133 error = 'await expression' 

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

1135 if self._normalizer.version < (3, 10): 

1136 error = 'operator' 

1137 else: 

1138 error = 'expression' 

1139 else: 

1140 # Has a trailer 

1141 trailer = node.children[-1] 

1142 assert trailer.type == 'trailer' 

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

1144 error = 'function call' 

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

1146 error = 'subscript' 

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

1148 error = 'attribute' 

1149 elif type_ == "fstring": 

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

1151 error = 'literal' 

1152 else: 

1153 error = "f-string expression" 

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

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

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

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

1158 or '_test' in type_ 

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

1160 if self._normalizer.version < (3, 10): 

1161 error = 'operator' 

1162 else: 

1163 error = 'expression' 

1164 elif type_ == "star_expr": 

1165 if is_deletion: 

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

1167 error = "starred" 

1168 else: 

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

1170 else: 

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

1172 ancestor = node.parent 

1173 else: 

1174 ancestor = _skip_parens_bottom_up(node) 

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

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

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

1178 self.add_issue(node, message=message) 

1179 

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

1181 

1182 if error is not None: 

1183 if is_namedexpr: 

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

1185 else: 

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

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

1188 self.add_issue(node, message=message) 

1189 

1190 

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

1192class _CompForRule(_CheckAssignmentRule): 

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

1194 

1195 def is_issue(self, node): 

1196 expr_list = node.children[1] 

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

1198 self._check_assignment(expr_list) 

1199 

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

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

1202 

1203 

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

1205class _ExprStmtRule(_CheckAssignmentRule): 

1206 message = "illegal expression for augmented assignment" 

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

1208 

1209 def is_issue(self, node): 

1210 augassign = node.children[1] 

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

1212 

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

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

1215 self._check_assignment(before_equal, is_aug_assign=is_aug_assign) 

1216 

1217 if is_aug_assign: 

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

1219 # a, a[b], a.b 

1220 

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

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

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

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

1225 ): 

1226 return False 

1227 

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

1229 return True 

1230 else: 

1231 self.add_issue( 

1232 node, 

1233 message=self.extended_message.format( 

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

1235 ), 

1236 ) 

1237 

1238 

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

1240class _WithItemRule(_CheckAssignmentRule): 

1241 def is_issue(self, with_item): 

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

1243 

1244 

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

1246class _DelStmtRule(_CheckAssignmentRule): 

1247 def is_issue(self, del_stmt): 

1248 child = del_stmt.children[1] 

1249 

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

1251 self._check_assignment(child, is_deletion=True) 

1252 

1253 

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

1255class _ExprListRule(_CheckAssignmentRule): 

1256 def is_issue(self, expr_list): 

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

1258 self._check_assignment(expr) 

1259 

1260 

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

1262class _ForStmtRule(_CheckAssignmentRule): 

1263 def is_issue(self, for_stmt): 

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

1265 expr_list = for_stmt.children[1] 

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

1267 self._check_assignment(expr_list) 

1268 

1269 

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

1271class _NamedExprRule(_CheckAssignmentRule): 

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

1273 

1274 def is_issue(self, namedexpr_test): 

1275 # assigned name 

1276 first = namedexpr_test.children[0] 

1277 

1278 def search_namedexpr_in_comp_for(node): 

1279 while True: 

1280 parent = node.parent 

1281 if parent is None: 

1282 return parent 

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

1284 return parent 

1285 node = parent 

1286 

1287 if search_namedexpr_in_comp_for(namedexpr_test): 

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

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

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

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

1292 self.add_issue(namedexpr_test, message=message) 

1293 

1294 # defined names 

1295 exprlist = list() 

1296 

1297 def process_comp_for(comp_for): 

1298 if comp_for.type == 'sync_comp_for': 

1299 comp = comp_for 

1300 elif comp_for.type == 'comp_for': 

1301 comp = comp_for.children[1] 

1302 exprlist.extend(_get_for_stmt_definition_exprs(comp)) 

1303 

1304 def search_all_comp_ancestors(node): 

1305 has_ancestors = False 

1306 while True: 

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

1308 if node is None: 

1309 break 

1310 for child in node.children: 

1311 if child.type in _COMP_FOR_TYPES: 

1312 process_comp_for(child) 

1313 has_ancestors = True 

1314 break 

1315 return has_ancestors 

1316 

1317 # check assignment expressions in comprehensions 

1318 search_all = search_all_comp_ancestors(namedexpr_test) 

1319 if search_all: 

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

1321 message = 'assignment expression within a comprehension ' \ 

1322 'cannot be used in a class body' 

1323 self.add_issue(namedexpr_test, message=message) 

1324 

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

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

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

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

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

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

1331 message = 'assignment expression cannot rebind ' \ 

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

1333 self.add_issue(namedexpr_test, message=message) 

1334 

1335 self._check_assignment(first, is_namedexpr=True)