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