Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/astroid/rebuilder.py: 46%
776 statements
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
« prev ^ index » next coverage.py v7.2.7, created at 2023-06-07 06:53 +0000
1# Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
2# For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
3# Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
5"""This module contains utilities for rebuilding an _ast tree in
6order to get a single Astroid representation.
7"""
9from __future__ import annotations
11import ast
12import sys
13import token
14from collections.abc import Callable, Generator
15from io import StringIO
16from tokenize import TokenInfo, generate_tokens
17from typing import TYPE_CHECKING, Final, TypeVar, Union, cast, overload
19from astroid import nodes
20from astroid._ast import ParserModule, get_parser_module, parse_function_type_comment
21from astroid.const import IS_PYPY, PY38, PY39_PLUS, Context
22from astroid.manager import AstroidManager
23from astroid.nodes import NodeNG
24from astroid.nodes.utils import Position
25from astroid.typing import SuccessfulInferenceResult
27REDIRECT: Final[dict[str, str]] = {
28 "arguments": "Arguments",
29 "comprehension": "Comprehension",
30 "ListCompFor": "Comprehension",
31 "GenExprFor": "Comprehension",
32 "excepthandler": "ExceptHandler",
33 "keyword": "Keyword",
34 "match_case": "MatchCase",
35}
38T_Doc = TypeVar(
39 "T_Doc",
40 "ast.Module",
41 "ast.ClassDef",
42 Union["ast.FunctionDef", "ast.AsyncFunctionDef"],
43)
44_FunctionT = TypeVar("_FunctionT", nodes.FunctionDef, nodes.AsyncFunctionDef)
45_ForT = TypeVar("_ForT", nodes.For, nodes.AsyncFor)
46_WithT = TypeVar("_WithT", nodes.With, nodes.AsyncWith)
47NodesWithDocsType = Union[nodes.Module, nodes.ClassDef, nodes.FunctionDef]
50# noinspection PyMethodMayBeStatic
51class TreeRebuilder:
52 """Rebuilds the _ast tree to become an Astroid tree."""
54 def __init__(
55 self,
56 manager: AstroidManager,
57 parser_module: ParserModule | None = None,
58 data: str | None = None,
59 ) -> None:
60 self._manager = manager
61 self._data = data.split("\n") if data else None
62 self._global_names: list[dict[str, list[nodes.Global]]] = []
63 self._import_from_nodes: list[nodes.ImportFrom] = []
64 self._delayed_assattr: list[nodes.AssignAttr] = []
65 self._visit_meths: dict[type[ast.AST], Callable[[ast.AST, NodeNG], NodeNG]] = {}
67 if parser_module is None:
68 self._parser_module = get_parser_module()
69 else:
70 self._parser_module = parser_module
72 def _get_doc(self, node: T_Doc) -> tuple[T_Doc, ast.Constant | ast.Str | None]:
73 """Return the doc ast node."""
74 try:
75 if node.body and isinstance(node.body[0], ast.Expr):
76 first_value = node.body[0].value
77 if isinstance(first_value, ast.Constant) and isinstance(
78 first_value.value, str
79 ):
80 doc_ast_node = first_value
81 node.body = node.body[1:]
82 # The ast parser of python < 3.8 sets col_offset of multi-line strings to -1
83 # as it is unable to determine the value correctly. We reset this to None.
84 if doc_ast_node.col_offset == -1:
85 doc_ast_node.col_offset = None
86 return node, doc_ast_node
87 except IndexError:
88 pass # ast built from scratch
89 return node, None
91 def _get_context(
92 self,
93 node: (
94 ast.Attribute
95 | ast.List
96 | ast.Name
97 | ast.Subscript
98 | ast.Starred
99 | ast.Tuple
100 ),
101 ) -> Context:
102 return self._parser_module.context_classes.get(type(node.ctx), Context.Load)
104 def _get_position_info(
105 self,
106 node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
107 parent: nodes.ClassDef | nodes.FunctionDef | nodes.AsyncFunctionDef,
108 ) -> Position | None:
109 """Return position information for ClassDef and FunctionDef nodes.
111 In contrast to AST positions, these only include the actual keyword(s)
112 and the class / function name.
114 >>> @decorator
115 >>> async def some_func(var: int) -> None:
116 >>> ^^^^^^^^^^^^^^^^^^^
117 """
118 if not self._data:
119 return None
120 end_lineno = node.end_lineno
121 if node.body:
122 end_lineno = node.body[0].lineno
123 # pylint: disable-next=unsubscriptable-object
124 data = "\n".join(self._data[node.lineno - 1 : end_lineno])
126 start_token: TokenInfo | None = None
127 keyword_tokens: tuple[int, ...] = (token.NAME,)
128 if isinstance(parent, nodes.AsyncFunctionDef):
129 search_token = "async"
130 elif isinstance(parent, nodes.FunctionDef):
131 search_token = "def"
132 else:
133 search_token = "class"
135 for t in generate_tokens(StringIO(data).readline):
136 if (
137 start_token is not None
138 and t.type == token.NAME
139 and t.string == node.name
140 ):
141 break
142 if t.type in keyword_tokens:
143 if t.string == search_token:
144 start_token = t
145 continue
146 if t.string in {"def"}:
147 continue
148 start_token = None
149 else:
150 return None
152 return Position(
153 lineno=node.lineno + start_token.start[0] - 1,
154 col_offset=start_token.start[1],
155 end_lineno=node.lineno + t.end[0] - 1,
156 end_col_offset=t.end[1],
157 )
159 def _reset_end_lineno(self, newnode: nodes.NodeNG) -> None:
160 """Reset end_lineno and end_col_offset attributes for PyPy 3.8.
162 For some nodes, these are either set to -1 or only partially assigned.
163 To keep consistency across astroid and pylint, reset all.
165 This has been fixed in PyPy 3.9.
166 For reference, an (incomplete) list of nodes with issues:
167 - ClassDef - For
168 - FunctionDef - While
169 - Call - If
170 - Decorators - TryExcept
171 - With - TryFinally
172 - Assign
173 """
174 newnode.end_lineno = None
175 newnode.end_col_offset = None
176 for child_node in newnode.get_children():
177 self._reset_end_lineno(child_node)
179 def visit_module(
180 self, node: ast.Module, modname: str, modpath: str, package: bool
181 ) -> nodes.Module:
182 """Visit a Module node by returning a fresh instance of it.
184 Note: Method not called by 'visit'
185 """
186 node, doc_ast_node = self._get_doc(node)
187 newnode = nodes.Module(
188 name=modname,
189 file=modpath,
190 path=[modpath],
191 package=package,
192 )
193 newnode.postinit(
194 [self.visit(child, newnode) for child in node.body],
195 doc_node=self.visit(doc_ast_node, newnode),
196 )
197 if IS_PYPY and PY38:
198 self._reset_end_lineno(newnode)
199 return newnode
201 if TYPE_CHECKING: # noqa: C901
203 @overload
204 def visit(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName:
205 ...
207 @overload
208 def visit(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments:
209 ...
211 @overload
212 def visit(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert:
213 ...
215 @overload
216 def visit(
217 self, node: ast.AsyncFunctionDef, parent: NodeNG
218 ) -> nodes.AsyncFunctionDef:
219 ...
221 @overload
222 def visit(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor:
223 ...
225 @overload
226 def visit(self, node: ast.Await, parent: NodeNG) -> nodes.Await:
227 ...
229 @overload
230 def visit(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith:
231 ...
233 @overload
234 def visit(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign:
235 ...
237 @overload
238 def visit(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign:
239 ...
241 @overload
242 def visit(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign:
243 ...
245 @overload
246 def visit(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp:
247 ...
249 @overload
250 def visit(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp:
251 ...
253 @overload
254 def visit(self, node: ast.Break, parent: NodeNG) -> nodes.Break:
255 ...
257 @overload
258 def visit(self, node: ast.Call, parent: NodeNG) -> nodes.Call:
259 ...
261 @overload
262 def visit(self, node: ast.ClassDef, parent: NodeNG) -> nodes.ClassDef:
263 ...
265 @overload
266 def visit(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue:
267 ...
269 @overload
270 def visit(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare:
271 ...
273 @overload
274 def visit(self, node: ast.comprehension, parent: NodeNG) -> nodes.Comprehension:
275 ...
277 @overload
278 def visit(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete:
279 ...
281 @overload
282 def visit(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict:
283 ...
285 @overload
286 def visit(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp:
287 ...
289 @overload
290 def visit(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr:
291 ...
293 @overload
294 def visit(self, node: ast.ExceptHandler, parent: NodeNG) -> nodes.ExceptHandler:
295 ...
297 @overload
298 def visit(self, node: ast.For, parent: NodeNG) -> nodes.For:
299 ...
301 @overload
302 def visit(self, node: ast.ImportFrom, parent: NodeNG) -> nodes.ImportFrom:
303 ...
305 @overload
306 def visit(self, node: ast.FunctionDef, parent: NodeNG) -> nodes.FunctionDef:
307 ...
309 @overload
310 def visit(self, node: ast.GeneratorExp, parent: NodeNG) -> nodes.GeneratorExp:
311 ...
313 @overload
314 def visit(self, node: ast.Attribute, parent: NodeNG) -> nodes.Attribute:
315 ...
317 @overload
318 def visit(self, node: ast.Global, parent: NodeNG) -> nodes.Global:
319 ...
321 @overload
322 def visit(self, node: ast.If, parent: NodeNG) -> nodes.If:
323 ...
325 @overload
326 def visit(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp:
327 ...
329 @overload
330 def visit(self, node: ast.Import, parent: NodeNG) -> nodes.Import:
331 ...
333 @overload
334 def visit(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr:
335 ...
337 @overload
338 def visit(
339 self, node: ast.FormattedValue, parent: NodeNG
340 ) -> nodes.FormattedValue:
341 ...
343 @overload
344 def visit(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr:
345 ...
347 if sys.version_info < (3, 9):
348 # Not used in Python 3.9+
349 @overload
350 def visit(self, node: ast.ExtSlice, parent: nodes.Subscript) -> nodes.Tuple:
351 ...
353 @overload
354 def visit(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG:
355 ...
357 @overload
358 def visit(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword:
359 ...
361 @overload
362 def visit(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda:
363 ...
365 @overload
366 def visit(self, node: ast.List, parent: NodeNG) -> nodes.List:
367 ...
369 @overload
370 def visit(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp:
371 ...
373 @overload
374 def visit(
375 self, node: ast.Name, parent: NodeNG
376 ) -> nodes.Name | nodes.Const | nodes.AssignName | nodes.DelName:
377 ...
379 @overload
380 def visit(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal:
381 ...
383 @overload
384 def visit(self, node: ast.Constant, parent: NodeNG) -> nodes.Const:
385 ...
387 @overload
388 def visit(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass:
389 ...
391 @overload
392 def visit(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise:
393 ...
395 @overload
396 def visit(self, node: ast.Return, parent: NodeNG) -> nodes.Return:
397 ...
399 @overload
400 def visit(self, node: ast.Set, parent: NodeNG) -> nodes.Set:
401 ...
403 @overload
404 def visit(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp:
405 ...
407 @overload
408 def visit(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice:
409 ...
411 @overload
412 def visit(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript:
413 ...
415 @overload
416 def visit(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred:
417 ...
419 @overload
420 def visit(
421 self, node: ast.Try, parent: NodeNG
422 ) -> nodes.TryExcept | nodes.TryFinally:
423 ...
425 if sys.version_info >= (3, 11):
427 @overload
428 def visit(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
429 ...
431 @overload
432 def visit(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple:
433 ...
435 @overload
436 def visit(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp:
437 ...
439 @overload
440 def visit(self, node: ast.While, parent: NodeNG) -> nodes.While:
441 ...
443 @overload
444 def visit(self, node: ast.With, parent: NodeNG) -> nodes.With:
445 ...
447 @overload
448 def visit(self, node: ast.Yield, parent: NodeNG) -> nodes.Yield:
449 ...
451 @overload
452 def visit(self, node: ast.YieldFrom, parent: NodeNG) -> nodes.YieldFrom:
453 ...
455 if sys.version_info >= (3, 10):
457 @overload
458 def visit(self, node: ast.Match, parent: NodeNG) -> nodes.Match:
459 ...
461 @overload
462 def visit(self, node: ast.match_case, parent: NodeNG) -> nodes.MatchCase:
463 ...
465 @overload
466 def visit(self, node: ast.MatchValue, parent: NodeNG) -> nodes.MatchValue:
467 ...
469 @overload
470 def visit(
471 self, node: ast.MatchSingleton, parent: NodeNG
472 ) -> nodes.MatchSingleton:
473 ...
475 @overload
476 def visit(
477 self, node: ast.MatchSequence, parent: NodeNG
478 ) -> nodes.MatchSequence:
479 ...
481 @overload
482 def visit(
483 self, node: ast.MatchMapping, parent: NodeNG
484 ) -> nodes.MatchMapping:
485 ...
487 @overload
488 def visit(self, node: ast.MatchClass, parent: NodeNG) -> nodes.MatchClass:
489 ...
491 @overload
492 def visit(self, node: ast.MatchStar, parent: NodeNG) -> nodes.MatchStar:
493 ...
495 @overload
496 def visit(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs:
497 ...
499 @overload
500 def visit(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr:
501 ...
503 @overload
504 def visit(self, node: ast.pattern, parent: NodeNG) -> nodes.Pattern:
505 ...
507 @overload
508 def visit(self, node: ast.AST, parent: NodeNG) -> NodeNG:
509 ...
511 @overload
512 def visit(self, node: None, parent: NodeNG) -> None:
513 ...
515 def visit(self, node: ast.AST | None, parent: NodeNG) -> NodeNG | None:
516 if node is None:
517 return None
518 cls = node.__class__
519 if cls in self._visit_meths:
520 visit_method = self._visit_meths[cls]
521 else:
522 cls_name = cls.__name__
523 visit_name = "visit_" + REDIRECT.get(cls_name, cls_name).lower()
524 visit_method = getattr(self, visit_name)
525 self._visit_meths[cls] = visit_method
526 return visit_method(node, parent)
528 def _save_assignment(self, node: nodes.AssignName | nodes.DelName) -> None:
529 """Save assignment situation since node.parent is not available yet."""
530 if self._global_names and node.name in self._global_names[-1]:
531 node.root().set_local(node.name, node)
532 else:
533 assert node.parent
534 assert node.name
535 node.parent.set_local(node.name, node)
537 def visit_arg(self, node: ast.arg, parent: NodeNG) -> nodes.AssignName:
538 """Visit an arg node by returning a fresh AssName instance."""
539 return self.visit_assignname(node, parent, node.arg)
541 def visit_arguments(self, node: ast.arguments, parent: NodeNG) -> nodes.Arguments:
542 """Visit an Arguments node by returning a fresh instance of it."""
543 vararg: str | None = None
544 kwarg: str | None = None
545 newnode = nodes.Arguments(
546 node.vararg.arg if node.vararg else None,
547 node.kwarg.arg if node.kwarg else None,
548 parent,
549 )
550 args = [self.visit(child, newnode) for child in node.args]
551 defaults = [self.visit(child, newnode) for child in node.defaults]
552 varargannotation: NodeNG | None = None
553 kwargannotation: NodeNG | None = None
554 if node.vararg:
555 vararg = node.vararg.arg
556 varargannotation = self.visit(node.vararg.annotation, newnode)
557 if node.kwarg:
558 kwarg = node.kwarg.arg
559 kwargannotation = self.visit(node.kwarg.annotation, newnode)
561 if PY38:
562 # In Python 3.8 'end_lineno' and 'end_col_offset'
563 # for 'kwonlyargs' don't include the annotation.
564 for arg in node.kwonlyargs:
565 if arg.annotation is not None:
566 arg.end_lineno = arg.annotation.end_lineno
567 arg.end_col_offset = arg.annotation.end_col_offset
569 kwonlyargs = [self.visit(child, newnode) for child in node.kwonlyargs]
570 kw_defaults = [self.visit(child, newnode) for child in node.kw_defaults]
571 annotations = [self.visit(arg.annotation, newnode) for arg in node.args]
572 kwonlyargs_annotations = [
573 self.visit(arg.annotation, newnode) for arg in node.kwonlyargs
574 ]
576 posonlyargs = [self.visit(child, newnode) for child in node.posonlyargs]
577 posonlyargs_annotations = [
578 self.visit(arg.annotation, newnode) for arg in node.posonlyargs
579 ]
580 type_comment_args = [
581 self.check_type_comment(child, parent=newnode) for child in node.args
582 ]
583 type_comment_kwonlyargs = [
584 self.check_type_comment(child, parent=newnode) for child in node.kwonlyargs
585 ]
586 type_comment_posonlyargs = [
587 self.check_type_comment(child, parent=newnode) for child in node.posonlyargs
588 ]
590 newnode.postinit(
591 args=args,
592 defaults=defaults,
593 kwonlyargs=kwonlyargs,
594 posonlyargs=posonlyargs,
595 kw_defaults=kw_defaults,
596 annotations=annotations,
597 kwonlyargs_annotations=kwonlyargs_annotations,
598 posonlyargs_annotations=posonlyargs_annotations,
599 varargannotation=varargannotation,
600 kwargannotation=kwargannotation,
601 type_comment_args=type_comment_args,
602 type_comment_kwonlyargs=type_comment_kwonlyargs,
603 type_comment_posonlyargs=type_comment_posonlyargs,
604 )
605 # save argument names in locals:
606 assert newnode.parent
607 if vararg:
608 newnode.parent.set_local(vararg, newnode)
609 if kwarg:
610 newnode.parent.set_local(kwarg, newnode)
611 return newnode
613 def visit_assert(self, node: ast.Assert, parent: NodeNG) -> nodes.Assert:
614 """Visit a Assert node by returning a fresh instance of it."""
615 newnode = nodes.Assert(
616 lineno=node.lineno,
617 col_offset=node.col_offset,
618 end_lineno=node.end_lineno,
619 end_col_offset=node.end_col_offset,
620 parent=parent,
621 )
622 msg: NodeNG | None = None
623 if node.msg:
624 msg = self.visit(node.msg, newnode)
625 newnode.postinit(self.visit(node.test, newnode), msg)
626 return newnode
628 def check_type_comment(
629 self,
630 node: (
631 ast.Assign | ast.arg | ast.For | ast.AsyncFor | ast.With | ast.AsyncWith
632 ),
633 parent: (
634 nodes.Assign
635 | nodes.Arguments
636 | nodes.For
637 | nodes.AsyncFor
638 | nodes.With
639 | nodes.AsyncWith
640 ),
641 ) -> NodeNG | None:
642 if not node.type_comment:
643 return None
645 try:
646 type_comment_ast = self._parser_module.parse(node.type_comment)
647 except SyntaxError:
648 # Invalid type comment, just skip it.
649 return None
651 # For '# type: # any comment' ast.parse returns a Module node,
652 # without any nodes in the body.
653 if not type_comment_ast.body:
654 return None
656 type_object = self.visit(type_comment_ast.body[0], parent=parent)
657 if not isinstance(type_object, nodes.Expr):
658 return None
660 return type_object.value
662 def check_function_type_comment(
663 self, node: ast.FunctionDef | ast.AsyncFunctionDef, parent: NodeNG
664 ) -> tuple[NodeNG | None, list[NodeNG]] | None:
665 if not node.type_comment:
666 return None
668 try:
669 type_comment_ast = parse_function_type_comment(node.type_comment)
670 except SyntaxError:
671 # Invalid type comment, just skip it.
672 return None
674 if not type_comment_ast:
675 return None
677 returns: NodeNG | None = None
678 argtypes: list[NodeNG] = [
679 self.visit(elem, parent) for elem in (type_comment_ast.argtypes or [])
680 ]
681 if type_comment_ast.returns:
682 returns = self.visit(type_comment_ast.returns, parent)
684 return returns, argtypes
686 def visit_asyncfunctiondef(
687 self, node: ast.AsyncFunctionDef, parent: NodeNG
688 ) -> nodes.AsyncFunctionDef:
689 return self._visit_functiondef(nodes.AsyncFunctionDef, node, parent)
691 def visit_asyncfor(self, node: ast.AsyncFor, parent: NodeNG) -> nodes.AsyncFor:
692 return self._visit_for(nodes.AsyncFor, node, parent)
694 def visit_await(self, node: ast.Await, parent: NodeNG) -> nodes.Await:
695 newnode = nodes.Await(
696 lineno=node.lineno,
697 col_offset=node.col_offset,
698 end_lineno=node.end_lineno,
699 end_col_offset=node.end_col_offset,
700 parent=parent,
701 )
702 newnode.postinit(value=self.visit(node.value, newnode))
703 return newnode
705 def visit_asyncwith(self, node: ast.AsyncWith, parent: NodeNG) -> nodes.AsyncWith:
706 return self._visit_with(nodes.AsyncWith, node, parent)
708 def visit_assign(self, node: ast.Assign, parent: NodeNG) -> nodes.Assign:
709 """Visit a Assign node by returning a fresh instance of it."""
710 newnode = nodes.Assign(
711 lineno=node.lineno,
712 col_offset=node.col_offset,
713 end_lineno=node.end_lineno,
714 end_col_offset=node.end_col_offset,
715 parent=parent,
716 )
717 type_annotation = self.check_type_comment(node, parent=newnode)
718 newnode.postinit(
719 targets=[self.visit(child, newnode) for child in node.targets],
720 value=self.visit(node.value, newnode),
721 type_annotation=type_annotation,
722 )
723 return newnode
725 def visit_annassign(self, node: ast.AnnAssign, parent: NodeNG) -> nodes.AnnAssign:
726 """Visit an AnnAssign node by returning a fresh instance of it."""
727 newnode = nodes.AnnAssign(
728 lineno=node.lineno,
729 col_offset=node.col_offset,
730 end_lineno=node.end_lineno,
731 end_col_offset=node.end_col_offset,
732 parent=parent,
733 )
734 newnode.postinit(
735 target=self.visit(node.target, newnode),
736 annotation=self.visit(node.annotation, newnode),
737 simple=node.simple,
738 value=self.visit(node.value, newnode),
739 )
740 return newnode
742 @overload
743 def visit_assignname(
744 self, node: ast.AST, parent: NodeNG, node_name: str
745 ) -> nodes.AssignName:
746 ...
748 @overload
749 def visit_assignname(self, node: ast.AST, parent: NodeNG, node_name: None) -> None:
750 ...
752 def visit_assignname(
753 self, node: ast.AST, parent: NodeNG, node_name: str | None
754 ) -> nodes.AssignName | None:
755 """Visit a node and return a AssignName node.
757 Note: Method not called by 'visit'
758 """
759 if node_name is None:
760 return None
761 newnode = nodes.AssignName(
762 name=node_name,
763 lineno=node.lineno,
764 col_offset=node.col_offset,
765 end_lineno=node.end_lineno,
766 end_col_offset=node.end_col_offset,
767 parent=parent,
768 )
769 self._save_assignment(newnode)
770 return newnode
772 def visit_augassign(self, node: ast.AugAssign, parent: NodeNG) -> nodes.AugAssign:
773 """Visit a AugAssign node by returning a fresh instance of it."""
774 newnode = nodes.AugAssign(
775 op=self._parser_module.bin_op_classes[type(node.op)] + "=",
776 lineno=node.lineno,
777 col_offset=node.col_offset,
778 end_lineno=node.end_lineno,
779 end_col_offset=node.end_col_offset,
780 parent=parent,
781 )
782 newnode.postinit(
783 self.visit(node.target, newnode), self.visit(node.value, newnode)
784 )
785 return newnode
787 def visit_binop(self, node: ast.BinOp, parent: NodeNG) -> nodes.BinOp:
788 """Visit a BinOp node by returning a fresh instance of it."""
789 newnode = nodes.BinOp(
790 op=self._parser_module.bin_op_classes[type(node.op)],
791 lineno=node.lineno,
792 col_offset=node.col_offset,
793 end_lineno=node.end_lineno,
794 end_col_offset=node.end_col_offset,
795 parent=parent,
796 )
797 newnode.postinit(
798 self.visit(node.left, newnode), self.visit(node.right, newnode)
799 )
800 return newnode
802 def visit_boolop(self, node: ast.BoolOp, parent: NodeNG) -> nodes.BoolOp:
803 """Visit a BoolOp node by returning a fresh instance of it."""
804 newnode = nodes.BoolOp(
805 op=self._parser_module.bool_op_classes[type(node.op)],
806 lineno=node.lineno,
807 col_offset=node.col_offset,
808 end_lineno=node.end_lineno,
809 end_col_offset=node.end_col_offset,
810 parent=parent,
811 )
812 newnode.postinit([self.visit(child, newnode) for child in node.values])
813 return newnode
815 def visit_break(self, node: ast.Break, parent: NodeNG) -> nodes.Break:
816 """Visit a Break node by returning a fresh instance of it."""
817 return nodes.Break(
818 lineno=node.lineno,
819 col_offset=node.col_offset,
820 end_lineno=node.end_lineno,
821 end_col_offset=node.end_col_offset,
822 parent=parent,
823 )
825 def visit_call(self, node: ast.Call, parent: NodeNG) -> nodes.Call:
826 """Visit a CallFunc node by returning a fresh instance of it."""
827 newnode = nodes.Call(
828 lineno=node.lineno,
829 col_offset=node.col_offset,
830 end_lineno=node.end_lineno,
831 end_col_offset=node.end_col_offset,
832 parent=parent,
833 )
834 newnode.postinit(
835 func=self.visit(node.func, newnode),
836 args=[self.visit(child, newnode) for child in node.args],
837 keywords=[self.visit(child, newnode) for child in node.keywords],
838 )
839 return newnode
841 def visit_classdef(
842 self, node: ast.ClassDef, parent: NodeNG, newstyle: bool = True
843 ) -> nodes.ClassDef:
844 """Visit a ClassDef node to become astroid."""
845 node, doc_ast_node = self._get_doc(node)
846 newnode = nodes.ClassDef(
847 name=node.name,
848 lineno=node.lineno,
849 col_offset=node.col_offset,
850 end_lineno=node.end_lineno,
851 end_col_offset=node.end_col_offset,
852 parent=parent,
853 )
854 metaclass = None
855 for keyword in node.keywords:
856 if keyword.arg == "metaclass":
857 metaclass = self.visit(keyword, newnode).value
858 break
859 decorators = self.visit_decorators(node, newnode)
860 newnode.postinit(
861 [self.visit(child, newnode) for child in node.bases],
862 [self.visit(child, newnode) for child in node.body],
863 decorators,
864 newstyle,
865 metaclass,
866 [
867 self.visit(kwd, newnode)
868 for kwd in node.keywords
869 if kwd.arg != "metaclass"
870 ],
871 position=self._get_position_info(node, newnode),
872 doc_node=self.visit(doc_ast_node, newnode),
873 )
874 return newnode
876 def visit_continue(self, node: ast.Continue, parent: NodeNG) -> nodes.Continue:
877 """Visit a Continue node by returning a fresh instance of it."""
878 return nodes.Continue(
879 lineno=node.lineno,
880 col_offset=node.col_offset,
881 end_lineno=node.end_lineno,
882 end_col_offset=node.end_col_offset,
883 parent=parent,
884 )
886 def visit_compare(self, node: ast.Compare, parent: NodeNG) -> nodes.Compare:
887 """Visit a Compare node by returning a fresh instance of it."""
888 newnode = nodes.Compare(
889 lineno=node.lineno,
890 col_offset=node.col_offset,
891 end_lineno=node.end_lineno,
892 end_col_offset=node.end_col_offset,
893 parent=parent,
894 )
895 newnode.postinit(
896 self.visit(node.left, newnode),
897 [
898 (
899 self._parser_module.cmp_op_classes[op.__class__],
900 self.visit(expr, newnode),
901 )
902 for (op, expr) in zip(node.ops, node.comparators)
903 ],
904 )
905 return newnode
907 def visit_comprehension(
908 self, node: ast.comprehension, parent: NodeNG
909 ) -> nodes.Comprehension:
910 """Visit a Comprehension node by returning a fresh instance of it."""
911 newnode = nodes.Comprehension(
912 parent=parent,
913 # Comprehension nodes don't have these attributes
914 # see https://docs.python.org/3/library/ast.html#abstract-grammar
915 lineno=None,
916 col_offset=None,
917 end_lineno=None,
918 end_col_offset=None,
919 )
920 newnode.postinit(
921 self.visit(node.target, newnode),
922 self.visit(node.iter, newnode),
923 [self.visit(child, newnode) for child in node.ifs],
924 bool(node.is_async),
925 )
926 return newnode
928 def visit_decorators(
929 self,
930 node: ast.ClassDef | ast.FunctionDef | ast.AsyncFunctionDef,
931 parent: NodeNG,
932 ) -> nodes.Decorators | None:
933 """Visit a Decorators node by returning a fresh instance of it.
935 Note: Method not called by 'visit'
936 """
937 if not node.decorator_list:
938 return None
939 # /!\ node is actually an _ast.FunctionDef node while
940 # parent is an astroid.nodes.FunctionDef node
942 # Set the line number of the first decorator for Python 3.8+.
943 lineno = node.decorator_list[0].lineno
944 end_lineno = node.decorator_list[-1].end_lineno
945 end_col_offset = node.decorator_list[-1].end_col_offset
947 newnode = nodes.Decorators(
948 lineno=lineno,
949 col_offset=node.col_offset,
950 end_lineno=end_lineno,
951 end_col_offset=end_col_offset,
952 parent=parent,
953 )
954 newnode.postinit([self.visit(child, newnode) for child in node.decorator_list])
955 return newnode
957 def visit_delete(self, node: ast.Delete, parent: NodeNG) -> nodes.Delete:
958 """Visit a Delete node by returning a fresh instance of it."""
959 newnode = nodes.Delete(
960 lineno=node.lineno,
961 col_offset=node.col_offset,
962 end_lineno=node.end_lineno,
963 end_col_offset=node.end_col_offset,
964 parent=parent,
965 )
966 newnode.postinit([self.visit(child, newnode) for child in node.targets])
967 return newnode
969 def _visit_dict_items(
970 self, node: ast.Dict, parent: NodeNG, newnode: nodes.Dict
971 ) -> Generator[tuple[NodeNG, NodeNG], None, None]:
972 for key, value in zip(node.keys, node.values):
973 rebuilt_key: NodeNG
974 rebuilt_value = self.visit(value, newnode)
975 if not key:
976 # Extended unpacking
977 rebuilt_key = nodes.DictUnpack(
978 lineno=rebuilt_value.lineno,
979 col_offset=rebuilt_value.col_offset,
980 end_lineno=rebuilt_value.end_lineno,
981 end_col_offset=rebuilt_value.end_col_offset,
982 parent=parent,
983 )
984 else:
985 rebuilt_key = self.visit(key, newnode)
986 yield rebuilt_key, rebuilt_value
988 def visit_dict(self, node: ast.Dict, parent: NodeNG) -> nodes.Dict:
989 """Visit a Dict node by returning a fresh instance of it."""
990 newnode = nodes.Dict(
991 lineno=node.lineno,
992 col_offset=node.col_offset,
993 end_lineno=node.end_lineno,
994 end_col_offset=node.end_col_offset,
995 parent=parent,
996 )
997 items: list[tuple[SuccessfulInferenceResult, SuccessfulInferenceResult]] = list(
998 self._visit_dict_items(node, parent, newnode)
999 )
1000 newnode.postinit(items)
1001 return newnode
1003 def visit_dictcomp(self, node: ast.DictComp, parent: NodeNG) -> nodes.DictComp:
1004 """Visit a DictComp node by returning a fresh instance of it."""
1005 newnode = nodes.DictComp(
1006 lineno=node.lineno,
1007 col_offset=node.col_offset,
1008 end_lineno=node.end_lineno,
1009 end_col_offset=node.end_col_offset,
1010 parent=parent,
1011 )
1012 newnode.postinit(
1013 self.visit(node.key, newnode),
1014 self.visit(node.value, newnode),
1015 [self.visit(child, newnode) for child in node.generators],
1016 )
1017 return newnode
1019 def visit_expr(self, node: ast.Expr, parent: NodeNG) -> nodes.Expr:
1020 """Visit a Expr node by returning a fresh instance of it."""
1021 newnode = nodes.Expr(
1022 lineno=node.lineno,
1023 col_offset=node.col_offset,
1024 end_lineno=node.end_lineno,
1025 end_col_offset=node.end_col_offset,
1026 parent=parent,
1027 )
1028 newnode.postinit(self.visit(node.value, newnode))
1029 return newnode
1031 def visit_excepthandler(
1032 self, node: ast.ExceptHandler, parent: NodeNG
1033 ) -> nodes.ExceptHandler:
1034 """Visit an ExceptHandler node by returning a fresh instance of it."""
1035 newnode = nodes.ExceptHandler(
1036 lineno=node.lineno,
1037 col_offset=node.col_offset,
1038 end_lineno=node.end_lineno,
1039 end_col_offset=node.end_col_offset,
1040 parent=parent,
1041 )
1042 newnode.postinit(
1043 self.visit(node.type, newnode),
1044 self.visit_assignname(node, newnode, node.name),
1045 [self.visit(child, newnode) for child in node.body],
1046 )
1047 return newnode
1049 @overload
1050 def _visit_for(
1051 self, cls: type[nodes.For], node: ast.For, parent: NodeNG
1052 ) -> nodes.For:
1053 ...
1055 @overload
1056 def _visit_for(
1057 self, cls: type[nodes.AsyncFor], node: ast.AsyncFor, parent: NodeNG
1058 ) -> nodes.AsyncFor:
1059 ...
1061 def _visit_for(
1062 self, cls: type[_ForT], node: ast.For | ast.AsyncFor, parent: NodeNG
1063 ) -> _ForT:
1064 """Visit a For node by returning a fresh instance of it."""
1065 col_offset = node.col_offset
1066 if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncFor) and self._data:
1067 # pylint: disable-next=unsubscriptable-object
1068 col_offset = self._data[node.lineno - 1].index("async")
1070 newnode = cls(
1071 lineno=node.lineno,
1072 col_offset=col_offset,
1073 end_lineno=node.end_lineno,
1074 end_col_offset=node.end_col_offset,
1075 parent=parent,
1076 )
1077 type_annotation = self.check_type_comment(node, parent=newnode)
1078 newnode.postinit(
1079 target=self.visit(node.target, newnode),
1080 iter=self.visit(node.iter, newnode),
1081 body=[self.visit(child, newnode) for child in node.body],
1082 orelse=[self.visit(child, newnode) for child in node.orelse],
1083 type_annotation=type_annotation,
1084 )
1085 return newnode
1087 def visit_for(self, node: ast.For, parent: NodeNG) -> nodes.For:
1088 return self._visit_for(nodes.For, node, parent)
1090 def visit_importfrom(
1091 self, node: ast.ImportFrom, parent: NodeNG
1092 ) -> nodes.ImportFrom:
1093 """Visit an ImportFrom node by returning a fresh instance of it."""
1094 names = [(alias.name, alias.asname) for alias in node.names]
1095 newnode = nodes.ImportFrom(
1096 fromname=node.module or "",
1097 names=names,
1098 level=node.level or None,
1099 lineno=node.lineno,
1100 col_offset=node.col_offset,
1101 end_lineno=node.end_lineno,
1102 end_col_offset=node.end_col_offset,
1103 parent=parent,
1104 )
1105 # store From names to add them to locals after building
1106 self._import_from_nodes.append(newnode)
1107 return newnode
1109 @overload
1110 def _visit_functiondef(
1111 self, cls: type[nodes.FunctionDef], node: ast.FunctionDef, parent: NodeNG
1112 ) -> nodes.FunctionDef:
1113 ...
1115 @overload
1116 def _visit_functiondef(
1117 self,
1118 cls: type[nodes.AsyncFunctionDef],
1119 node: ast.AsyncFunctionDef,
1120 parent: NodeNG,
1121 ) -> nodes.AsyncFunctionDef:
1122 ...
1124 def _visit_functiondef(
1125 self,
1126 cls: type[_FunctionT],
1127 node: ast.FunctionDef | ast.AsyncFunctionDef,
1128 parent: NodeNG,
1129 ) -> _FunctionT:
1130 """Visit an FunctionDef node to become astroid."""
1131 self._global_names.append({})
1132 node, doc_ast_node = self._get_doc(node)
1134 lineno = node.lineno
1135 if node.decorator_list:
1136 # Python 3.8 sets the line number of a decorated function
1137 # to be the actual line number of the function, but the
1138 # previous versions expected the decorator's line number instead.
1139 # We reset the function's line number to that of the
1140 # first decorator to maintain backward compatibility.
1141 # It's not ideal but this discrepancy was baked into
1142 # the framework for *years*.
1143 lineno = node.decorator_list[0].lineno
1145 newnode = cls(
1146 name=node.name,
1147 lineno=lineno,
1148 col_offset=node.col_offset,
1149 end_lineno=node.end_lineno,
1150 end_col_offset=node.end_col_offset,
1151 parent=parent,
1152 )
1153 decorators = self.visit_decorators(node, newnode)
1154 returns: NodeNG | None
1155 if node.returns:
1156 returns = self.visit(node.returns, newnode)
1157 else:
1158 returns = None
1160 type_comment_args = type_comment_returns = None
1161 type_comment_annotation = self.check_function_type_comment(node, newnode)
1162 if type_comment_annotation:
1163 type_comment_returns, type_comment_args = type_comment_annotation
1164 newnode.postinit(
1165 args=self.visit(node.args, newnode),
1166 body=[self.visit(child, newnode) for child in node.body],
1167 decorators=decorators,
1168 returns=returns,
1169 type_comment_returns=type_comment_returns,
1170 type_comment_args=type_comment_args,
1171 position=self._get_position_info(node, newnode),
1172 doc_node=self.visit(doc_ast_node, newnode),
1173 )
1174 self._global_names.pop()
1175 return newnode
1177 def visit_functiondef(
1178 self, node: ast.FunctionDef, parent: NodeNG
1179 ) -> nodes.FunctionDef:
1180 return self._visit_functiondef(nodes.FunctionDef, node, parent)
1182 def visit_generatorexp(
1183 self, node: ast.GeneratorExp, parent: NodeNG
1184 ) -> nodes.GeneratorExp:
1185 """Visit a GeneratorExp node by returning a fresh instance of it."""
1186 newnode = nodes.GeneratorExp(
1187 lineno=node.lineno,
1188 col_offset=node.col_offset,
1189 end_lineno=node.end_lineno,
1190 end_col_offset=node.end_col_offset,
1191 parent=parent,
1192 )
1193 newnode.postinit(
1194 self.visit(node.elt, newnode),
1195 [self.visit(child, newnode) for child in node.generators],
1196 )
1197 return newnode
1199 def visit_attribute(
1200 self, node: ast.Attribute, parent: NodeNG
1201 ) -> nodes.Attribute | nodes.AssignAttr | nodes.DelAttr:
1202 """Visit an Attribute node by returning a fresh instance of it."""
1203 context = self._get_context(node)
1204 newnode: nodes.Attribute | nodes.AssignAttr | nodes.DelAttr
1205 if context == Context.Del:
1206 # FIXME : maybe we should reintroduce and visit_delattr ?
1207 # for instance, deactivating assign_ctx
1208 newnode = nodes.DelAttr(
1209 attrname=node.attr,
1210 lineno=node.lineno,
1211 col_offset=node.col_offset,
1212 end_lineno=node.end_lineno,
1213 end_col_offset=node.end_col_offset,
1214 parent=parent,
1215 )
1216 elif context == Context.Store:
1217 newnode = nodes.AssignAttr(
1218 attrname=node.attr,
1219 lineno=node.lineno,
1220 col_offset=node.col_offset,
1221 end_lineno=node.end_lineno,
1222 end_col_offset=node.end_col_offset,
1223 parent=parent,
1224 )
1225 # Prohibit a local save if we are in an ExceptHandler.
1226 if not isinstance(parent, nodes.ExceptHandler):
1227 # mypy doesn't recognize that newnode has to be AssignAttr because it
1228 # doesn't support ParamSpec
1229 # See https://github.com/python/mypy/issues/8645
1230 self._delayed_assattr.append(newnode) # type: ignore[arg-type]
1231 else:
1232 newnode = nodes.Attribute(
1233 attrname=node.attr,
1234 lineno=node.lineno,
1235 col_offset=node.col_offset,
1236 end_lineno=node.end_lineno,
1237 end_col_offset=node.end_col_offset,
1238 parent=parent,
1239 )
1240 newnode.postinit(self.visit(node.value, newnode))
1241 return newnode
1243 def visit_global(self, node: ast.Global, parent: NodeNG) -> nodes.Global:
1244 """Visit a Global node to become astroid."""
1245 newnode = nodes.Global(
1246 names=node.names,
1247 lineno=node.lineno,
1248 col_offset=node.col_offset,
1249 end_lineno=node.end_lineno,
1250 end_col_offset=node.end_col_offset,
1251 parent=parent,
1252 )
1253 if self._global_names: # global at the module level, no effect
1254 for name in node.names:
1255 self._global_names[-1].setdefault(name, []).append(newnode)
1256 return newnode
1258 def visit_if(self, node: ast.If, parent: NodeNG) -> nodes.If:
1259 """Visit an If node by returning a fresh instance of it."""
1260 newnode = nodes.If(
1261 lineno=node.lineno,
1262 col_offset=node.col_offset,
1263 end_lineno=node.end_lineno,
1264 end_col_offset=node.end_col_offset,
1265 parent=parent,
1266 )
1267 newnode.postinit(
1268 self.visit(node.test, newnode),
1269 [self.visit(child, newnode) for child in node.body],
1270 [self.visit(child, newnode) for child in node.orelse],
1271 )
1272 return newnode
1274 def visit_ifexp(self, node: ast.IfExp, parent: NodeNG) -> nodes.IfExp:
1275 """Visit a IfExp node by returning a fresh instance of it."""
1276 newnode = nodes.IfExp(
1277 lineno=node.lineno,
1278 col_offset=node.col_offset,
1279 end_lineno=node.end_lineno,
1280 end_col_offset=node.end_col_offset,
1281 parent=parent,
1282 )
1283 newnode.postinit(
1284 self.visit(node.test, newnode),
1285 self.visit(node.body, newnode),
1286 self.visit(node.orelse, newnode),
1287 )
1288 return newnode
1290 def visit_import(self, node: ast.Import, parent: NodeNG) -> nodes.Import:
1291 """Visit a Import node by returning a fresh instance of it."""
1292 names = [(alias.name, alias.asname) for alias in node.names]
1293 newnode = nodes.Import(
1294 names=names,
1295 lineno=node.lineno,
1296 col_offset=node.col_offset,
1297 end_lineno=node.end_lineno,
1298 end_col_offset=node.end_col_offset,
1299 parent=parent,
1300 )
1301 # save import names in parent's locals:
1302 for name, asname in newnode.names:
1303 name = asname or name
1304 parent.set_local(name.split(".")[0], newnode)
1305 return newnode
1307 def visit_joinedstr(self, node: ast.JoinedStr, parent: NodeNG) -> nodes.JoinedStr:
1308 newnode = nodes.JoinedStr(
1309 lineno=node.lineno,
1310 col_offset=node.col_offset,
1311 end_lineno=node.end_lineno,
1312 end_col_offset=node.end_col_offset,
1313 parent=parent,
1314 )
1315 newnode.postinit([self.visit(child, newnode) for child in node.values])
1316 return newnode
1318 def visit_formattedvalue(
1319 self, node: ast.FormattedValue, parent: NodeNG
1320 ) -> nodes.FormattedValue:
1321 newnode = nodes.FormattedValue(
1322 lineno=node.lineno,
1323 col_offset=node.col_offset,
1324 end_lineno=node.end_lineno,
1325 end_col_offset=node.end_col_offset,
1326 parent=parent,
1327 )
1328 newnode.postinit(
1329 value=self.visit(node.value, newnode),
1330 conversion=node.conversion,
1331 format_spec=self.visit(node.format_spec, newnode),
1332 )
1333 return newnode
1335 def visit_namedexpr(self, node: ast.NamedExpr, parent: NodeNG) -> nodes.NamedExpr:
1336 newnode = nodes.NamedExpr(
1337 lineno=node.lineno,
1338 col_offset=node.col_offset,
1339 end_lineno=node.end_lineno,
1340 end_col_offset=node.end_col_offset,
1341 parent=parent,
1342 )
1343 newnode.postinit(
1344 self.visit(node.target, newnode), self.visit(node.value, newnode)
1345 )
1346 return newnode
1348 if sys.version_info < (3, 9):
1349 # Not used in Python 3.9+.
1350 def visit_extslice(
1351 self, node: ast.ExtSlice, parent: nodes.Subscript
1352 ) -> nodes.Tuple:
1353 """Visit an ExtSlice node by returning a fresh instance of Tuple."""
1354 # ExtSlice doesn't have lineno or col_offset information
1355 newnode = nodes.Tuple(ctx=Context.Load, parent=parent)
1356 newnode.postinit([self.visit(dim, newnode) for dim in node.dims])
1357 return newnode
1359 def visit_index(self, node: ast.Index, parent: nodes.Subscript) -> NodeNG:
1360 """Visit a Index node by returning a fresh instance of NodeNG."""
1361 return self.visit(node.value, parent)
1363 def visit_keyword(self, node: ast.keyword, parent: NodeNG) -> nodes.Keyword:
1364 """Visit a Keyword node by returning a fresh instance of it."""
1365 newnode = nodes.Keyword(
1366 arg=node.arg,
1367 # position attributes added in 3.9
1368 lineno=getattr(node, "lineno", None),
1369 col_offset=getattr(node, "col_offset", None),
1370 end_lineno=getattr(node, "end_lineno", None),
1371 end_col_offset=getattr(node, "end_col_offset", None),
1372 parent=parent,
1373 )
1374 newnode.postinit(self.visit(node.value, newnode))
1375 return newnode
1377 def visit_lambda(self, node: ast.Lambda, parent: NodeNG) -> nodes.Lambda:
1378 """Visit a Lambda node by returning a fresh instance of it."""
1379 newnode = nodes.Lambda(
1380 lineno=node.lineno,
1381 col_offset=node.col_offset,
1382 end_lineno=node.end_lineno,
1383 end_col_offset=node.end_col_offset,
1384 parent=parent,
1385 )
1386 newnode.postinit(self.visit(node.args, newnode), self.visit(node.body, newnode))
1387 return newnode
1389 def visit_list(self, node: ast.List, parent: NodeNG) -> nodes.List:
1390 """Visit a List node by returning a fresh instance of it."""
1391 context = self._get_context(node)
1392 newnode = nodes.List(
1393 ctx=context,
1394 lineno=node.lineno,
1395 col_offset=node.col_offset,
1396 end_lineno=node.end_lineno,
1397 end_col_offset=node.end_col_offset,
1398 parent=parent,
1399 )
1400 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1401 return newnode
1403 def visit_listcomp(self, node: ast.ListComp, parent: NodeNG) -> nodes.ListComp:
1404 """Visit a ListComp node by returning a fresh instance of it."""
1405 newnode = nodes.ListComp(
1406 lineno=node.lineno,
1407 col_offset=node.col_offset,
1408 end_lineno=node.end_lineno,
1409 end_col_offset=node.end_col_offset,
1410 parent=parent,
1411 )
1412 newnode.postinit(
1413 self.visit(node.elt, newnode),
1414 [self.visit(child, newnode) for child in node.generators],
1415 )
1416 return newnode
1418 def visit_name(
1419 self, node: ast.Name, parent: NodeNG
1420 ) -> nodes.Name | nodes.AssignName | nodes.DelName:
1421 """Visit a Name node by returning a fresh instance of it."""
1422 context = self._get_context(node)
1423 newnode: nodes.Name | nodes.AssignName | nodes.DelName
1424 if context == Context.Del:
1425 newnode = nodes.DelName(
1426 name=node.id,
1427 lineno=node.lineno,
1428 col_offset=node.col_offset,
1429 end_lineno=node.end_lineno,
1430 end_col_offset=node.end_col_offset,
1431 parent=parent,
1432 )
1433 elif context == Context.Store:
1434 newnode = nodes.AssignName(
1435 name=node.id,
1436 lineno=node.lineno,
1437 col_offset=node.col_offset,
1438 end_lineno=node.end_lineno,
1439 end_col_offset=node.end_col_offset,
1440 parent=parent,
1441 )
1442 else:
1443 newnode = nodes.Name(
1444 name=node.id,
1445 lineno=node.lineno,
1446 col_offset=node.col_offset,
1447 end_lineno=node.end_lineno,
1448 end_col_offset=node.end_col_offset,
1449 parent=parent,
1450 )
1451 # XXX REMOVE me :
1452 if context in (Context.Del, Context.Store): # 'Aug' ??
1453 newnode = cast(Union[nodes.AssignName, nodes.DelName], newnode)
1454 self._save_assignment(newnode)
1455 return newnode
1457 def visit_nonlocal(self, node: ast.Nonlocal, parent: NodeNG) -> nodes.Nonlocal:
1458 """Visit a Nonlocal node and return a new instance of it."""
1459 return nodes.Nonlocal(
1460 names=node.names,
1461 lineno=node.lineno,
1462 col_offset=node.col_offset,
1463 end_lineno=node.end_lineno,
1464 end_col_offset=node.end_col_offset,
1465 parent=parent,
1466 )
1468 def visit_constant(self, node: ast.Constant, parent: NodeNG) -> nodes.Const:
1469 """Visit a Constant node by returning a fresh instance of Const."""
1470 return nodes.Const(
1471 value=node.value,
1472 kind=node.kind,
1473 lineno=node.lineno,
1474 col_offset=node.col_offset,
1475 end_lineno=node.end_lineno,
1476 end_col_offset=node.end_col_offset,
1477 parent=parent,
1478 )
1480 def visit_pass(self, node: ast.Pass, parent: NodeNG) -> nodes.Pass:
1481 """Visit a Pass node by returning a fresh instance of it."""
1482 return nodes.Pass(
1483 lineno=node.lineno,
1484 col_offset=node.col_offset,
1485 end_lineno=node.end_lineno,
1486 end_col_offset=node.end_col_offset,
1487 parent=parent,
1488 )
1490 def visit_raise(self, node: ast.Raise, parent: NodeNG) -> nodes.Raise:
1491 """Visit a Raise node by returning a fresh instance of it."""
1492 newnode = nodes.Raise(
1493 lineno=node.lineno,
1494 col_offset=node.col_offset,
1495 end_lineno=node.end_lineno,
1496 end_col_offset=node.end_col_offset,
1497 parent=parent,
1498 )
1499 # no traceback; anyway it is not used in Pylint
1500 newnode.postinit(
1501 exc=self.visit(node.exc, newnode),
1502 cause=self.visit(node.cause, newnode),
1503 )
1504 return newnode
1506 def visit_return(self, node: ast.Return, parent: NodeNG) -> nodes.Return:
1507 """Visit a Return node by returning a fresh instance of it."""
1508 newnode = nodes.Return(
1509 lineno=node.lineno,
1510 col_offset=node.col_offset,
1511 end_lineno=node.end_lineno,
1512 end_col_offset=node.end_col_offset,
1513 parent=parent,
1514 )
1515 newnode.postinit(self.visit(node.value, newnode))
1516 return newnode
1518 def visit_set(self, node: ast.Set, parent: NodeNG) -> nodes.Set:
1519 """Visit a Set node by returning a fresh instance of it."""
1520 newnode = nodes.Set(
1521 lineno=node.lineno,
1522 col_offset=node.col_offset,
1523 end_lineno=node.end_lineno,
1524 end_col_offset=node.end_col_offset,
1525 parent=parent,
1526 )
1527 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1528 return newnode
1530 def visit_setcomp(self, node: ast.SetComp, parent: NodeNG) -> nodes.SetComp:
1531 """Visit a SetComp node by returning a fresh instance of it."""
1532 newnode = nodes.SetComp(
1533 lineno=node.lineno,
1534 col_offset=node.col_offset,
1535 end_lineno=node.end_lineno,
1536 end_col_offset=node.end_col_offset,
1537 parent=parent,
1538 )
1539 newnode.postinit(
1540 self.visit(node.elt, newnode),
1541 [self.visit(child, newnode) for child in node.generators],
1542 )
1543 return newnode
1545 def visit_slice(self, node: ast.Slice, parent: nodes.Subscript) -> nodes.Slice:
1546 """Visit a Slice node by returning a fresh instance of it."""
1547 newnode = nodes.Slice(
1548 # position attributes added in 3.9
1549 lineno=getattr(node, "lineno", None),
1550 col_offset=getattr(node, "col_offset", None),
1551 end_lineno=getattr(node, "end_lineno", None),
1552 end_col_offset=getattr(node, "end_col_offset", None),
1553 parent=parent,
1554 )
1555 newnode.postinit(
1556 lower=self.visit(node.lower, newnode),
1557 upper=self.visit(node.upper, newnode),
1558 step=self.visit(node.step, newnode),
1559 )
1560 return newnode
1562 def visit_subscript(self, node: ast.Subscript, parent: NodeNG) -> nodes.Subscript:
1563 """Visit a Subscript node by returning a fresh instance of it."""
1564 context = self._get_context(node)
1565 newnode = nodes.Subscript(
1566 ctx=context,
1567 lineno=node.lineno,
1568 col_offset=node.col_offset,
1569 end_lineno=node.end_lineno,
1570 end_col_offset=node.end_col_offset,
1571 parent=parent,
1572 )
1573 newnode.postinit(
1574 self.visit(node.value, newnode), self.visit(node.slice, newnode)
1575 )
1576 return newnode
1578 def visit_starred(self, node: ast.Starred, parent: NodeNG) -> nodes.Starred:
1579 """Visit a Starred node and return a new instance of it."""
1580 context = self._get_context(node)
1581 newnode = nodes.Starred(
1582 ctx=context,
1583 lineno=node.lineno,
1584 col_offset=node.col_offset,
1585 end_lineno=node.end_lineno,
1586 end_col_offset=node.end_col_offset,
1587 parent=parent,
1588 )
1589 newnode.postinit(self.visit(node.value, newnode))
1590 return newnode
1592 def visit_tryexcept(self, node: ast.Try, parent: NodeNG) -> nodes.TryExcept:
1593 """Visit a TryExcept node by returning a fresh instance of it."""
1594 # TryExcept excludes the 'finally' but that will be included in the
1595 # end_lineno from 'node'. Therefore, we check all non 'finally'
1596 # children to find the correct end_lineno and column.
1597 end_lineno = node.end_lineno
1598 end_col_offset = node.end_col_offset
1599 all_children: list[ast.AST] = [*node.body, *node.handlers, *node.orelse]
1600 for child in reversed(all_children):
1601 end_lineno = child.end_lineno
1602 end_col_offset = child.end_col_offset
1603 break
1604 newnode = nodes.TryExcept(
1605 lineno=node.lineno,
1606 col_offset=node.col_offset,
1607 end_lineno=end_lineno,
1608 end_col_offset=end_col_offset,
1609 parent=parent,
1610 )
1611 newnode.postinit(
1612 [self.visit(child, newnode) for child in node.body],
1613 [self.visit(child, newnode) for child in node.handlers],
1614 [self.visit(child, newnode) for child in node.orelse],
1615 )
1616 return newnode
1618 def visit_try(
1619 self, node: ast.Try, parent: NodeNG
1620 ) -> nodes.TryExcept | nodes.TryFinally | None:
1621 # python 3.3 introduce a new Try node replacing
1622 # TryFinally/TryExcept nodes
1623 if node.finalbody:
1624 newnode = nodes.TryFinally(
1625 lineno=node.lineno,
1626 col_offset=node.col_offset,
1627 end_lineno=node.end_lineno,
1628 end_col_offset=node.end_col_offset,
1629 parent=parent,
1630 )
1631 body: list[NodeNG | nodes.TryExcept]
1632 if node.handlers:
1633 body = [self.visit_tryexcept(node, newnode)]
1634 else:
1635 body = [self.visit(child, newnode) for child in node.body]
1636 newnode.postinit(body, [self.visit(n, newnode) for n in node.finalbody])
1637 return newnode
1638 if node.handlers:
1639 return self.visit_tryexcept(node, parent)
1640 return None
1642 def visit_trystar(self, node: ast.TryStar, parent: NodeNG) -> nodes.TryStar:
1643 newnode = nodes.TryStar(
1644 lineno=node.lineno,
1645 col_offset=node.col_offset,
1646 end_lineno=node.end_lineno,
1647 end_col_offset=node.end_col_offset,
1648 parent=parent,
1649 )
1650 newnode.postinit(
1651 body=[self.visit(n, newnode) for n in node.body],
1652 handlers=[self.visit(n, newnode) for n in node.handlers],
1653 orelse=[self.visit(n, newnode) for n in node.orelse],
1654 finalbody=[self.visit(n, newnode) for n in node.finalbody],
1655 )
1656 return newnode
1658 def visit_tuple(self, node: ast.Tuple, parent: NodeNG) -> nodes.Tuple:
1659 """Visit a Tuple node by returning a fresh instance of it."""
1660 context = self._get_context(node)
1661 newnode = nodes.Tuple(
1662 ctx=context,
1663 lineno=node.lineno,
1664 col_offset=node.col_offset,
1665 end_lineno=node.end_lineno,
1666 end_col_offset=node.end_col_offset,
1667 parent=parent,
1668 )
1669 newnode.postinit([self.visit(child, newnode) for child in node.elts])
1670 return newnode
1672 def visit_unaryop(self, node: ast.UnaryOp, parent: NodeNG) -> nodes.UnaryOp:
1673 """Visit a UnaryOp node by returning a fresh instance of it."""
1674 newnode = nodes.UnaryOp(
1675 op=self._parser_module.unary_op_classes[node.op.__class__],
1676 lineno=node.lineno,
1677 col_offset=node.col_offset,
1678 end_lineno=node.end_lineno,
1679 end_col_offset=node.end_col_offset,
1680 parent=parent,
1681 )
1682 newnode.postinit(self.visit(node.operand, newnode))
1683 return newnode
1685 def visit_while(self, node: ast.While, parent: NodeNG) -> nodes.While:
1686 """Visit a While node by returning a fresh instance of it."""
1687 newnode = nodes.While(
1688 lineno=node.lineno,
1689 col_offset=node.col_offset,
1690 end_lineno=node.end_lineno,
1691 end_col_offset=node.end_col_offset,
1692 parent=parent,
1693 )
1694 newnode.postinit(
1695 self.visit(node.test, newnode),
1696 [self.visit(child, newnode) for child in node.body],
1697 [self.visit(child, newnode) for child in node.orelse],
1698 )
1699 return newnode
1701 @overload
1702 def _visit_with(
1703 self, cls: type[nodes.With], node: ast.With, parent: NodeNG
1704 ) -> nodes.With:
1705 ...
1707 @overload
1708 def _visit_with(
1709 self, cls: type[nodes.AsyncWith], node: ast.AsyncWith, parent: NodeNG
1710 ) -> nodes.AsyncWith:
1711 ...
1713 def _visit_with(
1714 self,
1715 cls: type[_WithT],
1716 node: ast.With | ast.AsyncWith,
1717 parent: NodeNG,
1718 ) -> _WithT:
1719 col_offset = node.col_offset
1720 if IS_PYPY and not PY39_PLUS and isinstance(node, ast.AsyncWith) and self._data:
1721 # pylint: disable-next=unsubscriptable-object
1722 col_offset = self._data[node.lineno - 1].index("async")
1724 newnode = cls(
1725 lineno=node.lineno,
1726 col_offset=col_offset,
1727 end_lineno=node.end_lineno,
1728 end_col_offset=node.end_col_offset,
1729 parent=parent,
1730 )
1732 def visit_child(child: ast.withitem) -> tuple[NodeNG, NodeNG | None]:
1733 expr = self.visit(child.context_expr, newnode)
1734 var = self.visit(child.optional_vars, newnode)
1735 return expr, var
1737 type_annotation = self.check_type_comment(node, parent=newnode)
1738 newnode.postinit(
1739 items=[visit_child(child) for child in node.items],
1740 body=[self.visit(child, newnode) for child in node.body],
1741 type_annotation=type_annotation,
1742 )
1743 return newnode
1745 def visit_with(self, node: ast.With, parent: NodeNG) -> NodeNG:
1746 return self._visit_with(nodes.With, node, parent)
1748 def visit_yield(self, node: ast.Yield, parent: NodeNG) -> NodeNG:
1749 """Visit a Yield node by returning a fresh instance of it."""
1750 newnode = nodes.Yield(
1751 lineno=node.lineno,
1752 col_offset=node.col_offset,
1753 end_lineno=node.end_lineno,
1754 end_col_offset=node.end_col_offset,
1755 parent=parent,
1756 )
1757 newnode.postinit(self.visit(node.value, newnode))
1758 return newnode
1760 def visit_yieldfrom(self, node: ast.YieldFrom, parent: NodeNG) -> NodeNG:
1761 newnode = nodes.YieldFrom(
1762 lineno=node.lineno,
1763 col_offset=node.col_offset,
1764 end_lineno=node.end_lineno,
1765 end_col_offset=node.end_col_offset,
1766 parent=parent,
1767 )
1768 newnode.postinit(self.visit(node.value, newnode))
1769 return newnode
1771 if sys.version_info >= (3, 10):
1773 def visit_match(self, node: ast.Match, parent: NodeNG) -> nodes.Match:
1774 newnode = nodes.Match(
1775 lineno=node.lineno,
1776 col_offset=node.col_offset,
1777 end_lineno=node.end_lineno,
1778 end_col_offset=node.end_col_offset,
1779 parent=parent,
1780 )
1781 newnode.postinit(
1782 subject=self.visit(node.subject, newnode),
1783 cases=[self.visit(case, newnode) for case in node.cases],
1784 )
1785 return newnode
1787 def visit_matchcase(
1788 self, node: ast.match_case, parent: NodeNG
1789 ) -> nodes.MatchCase:
1790 newnode = nodes.MatchCase(parent=parent)
1791 newnode.postinit(
1792 pattern=self.visit(node.pattern, newnode),
1793 guard=self.visit(node.guard, newnode),
1794 body=[self.visit(child, newnode) for child in node.body],
1795 )
1796 return newnode
1798 def visit_matchvalue(
1799 self, node: ast.MatchValue, parent: NodeNG
1800 ) -> nodes.MatchValue:
1801 newnode = nodes.MatchValue(
1802 lineno=node.lineno,
1803 col_offset=node.col_offset,
1804 end_lineno=node.end_lineno,
1805 end_col_offset=node.end_col_offset,
1806 parent=parent,
1807 )
1808 newnode.postinit(value=self.visit(node.value, newnode))
1809 return newnode
1811 def visit_matchsingleton(
1812 self, node: ast.MatchSingleton, parent: NodeNG
1813 ) -> nodes.MatchSingleton:
1814 return nodes.MatchSingleton(
1815 value=node.value,
1816 lineno=node.lineno,
1817 col_offset=node.col_offset,
1818 end_lineno=node.end_lineno,
1819 end_col_offset=node.end_col_offset,
1820 parent=parent,
1821 )
1823 def visit_matchsequence(
1824 self, node: ast.MatchSequence, parent: NodeNG
1825 ) -> nodes.MatchSequence:
1826 newnode = nodes.MatchSequence(
1827 lineno=node.lineno,
1828 col_offset=node.col_offset,
1829 end_lineno=node.end_lineno,
1830 end_col_offset=node.end_col_offset,
1831 parent=parent,
1832 )
1833 newnode.postinit(
1834 patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
1835 )
1836 return newnode
1838 def visit_matchmapping(
1839 self, node: ast.MatchMapping, parent: NodeNG
1840 ) -> nodes.MatchMapping:
1841 newnode = nodes.MatchMapping(
1842 lineno=node.lineno,
1843 col_offset=node.col_offset,
1844 end_lineno=node.end_lineno,
1845 end_col_offset=node.end_col_offset,
1846 parent=parent,
1847 )
1848 # Add AssignName node for 'node.name'
1849 # https://bugs.python.org/issue43994
1850 newnode.postinit(
1851 keys=[self.visit(child, newnode) for child in node.keys],
1852 patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
1853 rest=self.visit_assignname(node, newnode, node.rest),
1854 )
1855 return newnode
1857 def visit_matchclass(
1858 self, node: ast.MatchClass, parent: NodeNG
1859 ) -> nodes.MatchClass:
1860 newnode = nodes.MatchClass(
1861 lineno=node.lineno,
1862 col_offset=node.col_offset,
1863 end_lineno=node.end_lineno,
1864 end_col_offset=node.end_col_offset,
1865 parent=parent,
1866 )
1867 newnode.postinit(
1868 cls=self.visit(node.cls, newnode),
1869 patterns=[self.visit(pattern, newnode) for pattern in node.patterns],
1870 kwd_attrs=node.kwd_attrs,
1871 kwd_patterns=[
1872 self.visit(pattern, newnode) for pattern in node.kwd_patterns
1873 ],
1874 )
1875 return newnode
1877 def visit_matchstar(
1878 self, node: ast.MatchStar, parent: NodeNG
1879 ) -> nodes.MatchStar:
1880 newnode = nodes.MatchStar(
1881 lineno=node.lineno,
1882 col_offset=node.col_offset,
1883 end_lineno=node.end_lineno,
1884 end_col_offset=node.end_col_offset,
1885 parent=parent,
1886 )
1887 # Add AssignName node for 'node.name'
1888 # https://bugs.python.org/issue43994
1889 newnode.postinit(name=self.visit_assignname(node, newnode, node.name))
1890 return newnode
1892 def visit_matchas(self, node: ast.MatchAs, parent: NodeNG) -> nodes.MatchAs:
1893 newnode = nodes.MatchAs(
1894 lineno=node.lineno,
1895 col_offset=node.col_offset,
1896 end_lineno=node.end_lineno,
1897 end_col_offset=node.end_col_offset,
1898 parent=parent,
1899 )
1900 # Add AssignName node for 'node.name'
1901 # https://bugs.python.org/issue43994
1902 newnode.postinit(
1903 pattern=self.visit(node.pattern, newnode),
1904 name=self.visit_assignname(node, newnode, node.name),
1905 )
1906 return newnode
1908 def visit_matchor(self, node: ast.MatchOr, parent: NodeNG) -> nodes.MatchOr:
1909 newnode = nodes.MatchOr(
1910 lineno=node.lineno,
1911 col_offset=node.col_offset,
1912 end_lineno=node.end_lineno,
1913 end_col_offset=node.end_col_offset,
1914 parent=parent,
1915 )
1916 newnode.postinit(
1917 patterns=[self.visit(pattern, newnode) for pattern in node.patterns]
1918 )
1919 return newnode