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
5from __future__ import annotations
6
7import pprint
8import sys
9from collections.abc import Generator, Iterator
10from functools import cached_property
11from functools import singledispatch as _singledispatch
12from typing import (
13 TYPE_CHECKING,
14 Any,
15 ClassVar,
16 TypeVar,
17 cast,
18 overload,
19)
20
21from astroid import nodes, util
22from astroid.context import InferenceContext
23from astroid.exceptions import (
24 AstroidError,
25 InferenceError,
26 ParentMissingError,
27 StatementMissing,
28 UseInferenceDefault,
29)
30from astroid.manager import AstroidManager
31from astroid.nodes.as_string import AsStringVisitor
32from astroid.nodes.const import OP_PRECEDENCE
33from astroid.nodes.utils import Position
34from astroid.typing import InferenceErrorInfo, InferenceResult, InferFn
35
36if sys.version_info >= (3, 11):
37 from typing import Self
38else:
39 from typing_extensions import Self
40
41
42if TYPE_CHECKING:
43 from astroid.nodes import _base_nodes
44
45
46# Types for 'NodeNG.nodes_of_class()'
47_NodesT = TypeVar("_NodesT", bound="NodeNG")
48_NodesT2 = TypeVar("_NodesT2", bound="NodeNG")
49_NodesT3 = TypeVar("_NodesT3", bound="NodeNG")
50SkipKlassT = None | type["NodeNG"] | tuple[type["NodeNG"], ...]
51
52
53class NodeNG:
54 """A node of the new Abstract Syntax Tree (AST).
55
56 This is the base class for all Astroid node classes.
57 """
58
59 is_statement: ClassVar[bool] = False
60 """Whether this node indicates a statement."""
61 optional_assign: ClassVar[bool] = (
62 False # True for For (and for Comprehension if py <3.0)
63 )
64 """Whether this node optionally assigns a variable.
65
66 This is for loop assignments because loop won't necessarily perform an
67 assignment if the loop has no iterations.
68 This is also the case from comprehensions in Python 2.
69 """
70 is_function: ClassVar[bool] = False # True for FunctionDef nodes
71 """Whether this node indicates a function."""
72 is_lambda: ClassVar[bool] = False
73
74 # Attributes below are set by the builder module or by raw factories
75 _astroid_fields: ClassVar[tuple[str, ...]] = ()
76 """Node attributes that contain child nodes.
77
78 This is redefined in most concrete classes.
79 """
80 _other_fields: ClassVar[tuple[str, ...]] = ()
81 """Node attributes that do not contain child nodes."""
82 _other_other_fields: ClassVar[tuple[str, ...]] = ()
83 """Attributes that contain AST-dependent fields."""
84 # instance specific inference function infer(node, context)
85 _explicit_inference: InferFn[Self] | None = None
86
87 def __init__(
88 self,
89 lineno: int | None,
90 col_offset: int | None,
91 parent: NodeNG | None,
92 *,
93 end_lineno: int | None,
94 end_col_offset: int | None,
95 ) -> None:
96 self.lineno = lineno
97 """The line that this node appears on in the source code."""
98
99 self.col_offset = col_offset
100 """The column that this node appears on in the source code."""
101
102 self.parent = parent
103 """The parent node in the syntax tree."""
104
105 self.end_lineno = end_lineno
106 """The last line this node appears on in the source code."""
107
108 self.end_col_offset = end_col_offset
109 """The end column this node appears on in the source code.
110
111 Note: This is after the last symbol.
112 """
113
114 self.position: Position | None = None
115 """Position of keyword(s) and name.
116
117 Used as fallback for block nodes which might not provide good
118 enough positional information. E.g. ClassDef, FunctionDef.
119 """
120
121 def infer(
122 self, context: InferenceContext | None = None, **kwargs: Any
123 ) -> Generator[InferenceResult]:
124 """Get a generator of the inferred values.
125
126 This is the main entry point to the inference system.
127
128 .. seealso:: :ref:`inference`
129
130 If the instance has some explicit inference function set, it will be
131 called instead of the default interface.
132
133 :returns: The inferred values.
134 :rtype: iterable
135 """
136 if context is None:
137 context = InferenceContext()
138 else:
139 context = context.extra_context.get(self, context)
140 if self._explicit_inference is not None:
141 # explicit_inference is not bound, give it self explicitly
142 try:
143 for result in self._explicit_inference(
144 self, # type: ignore[arg-type]
145 context,
146 **kwargs,
147 ):
148 context.nodes_inferred += 1
149 yield result
150 return
151 except UseInferenceDefault:
152 pass
153
154 key = (self, context.lookupname, context.callcontext, context.boundnode)
155 if key in context.inferred:
156 yield from context.inferred[key]
157 return
158
159 results = []
160
161 # Limit inference amount to help with performance issues with
162 # exponentially exploding possible results.
163 limit = AstroidManager().max_inferable_values
164 for i, result in enumerate(self._infer(context=context, **kwargs)):
165 if i >= limit or (context.nodes_inferred > context.max_inferred):
166 results.append(util.Uninferable)
167 yield util.Uninferable
168 break
169 results.append(result)
170 yield result
171 context.nodes_inferred += 1
172
173 # Cache generated results for subsequent inferences of the
174 # same node using the same context
175 context.inferred[key] = tuple(results)
176 return
177
178 def repr_name(self) -> str:
179 """Get a name for nice representation.
180
181 This is either :attr:`name`, :attr:`attrname`, or the empty string.
182 """
183 if all(name not in self._astroid_fields for name in ("name", "attrname")):
184 return getattr(self, "name", "") or getattr(self, "attrname", "")
185 return ""
186
187 def __str__(self) -> str:
188 rname = self.repr_name()
189 cname = type(self).__name__
190 if rname:
191 string = "%(cname)s.%(rname)s(%(fields)s)"
192 alignment = len(cname) + len(rname) + 2
193 else:
194 string = "%(cname)s(%(fields)s)"
195 alignment = len(cname) + 1
196 result = []
197 for field in self._other_fields + self._astroid_fields:
198 value = getattr(self, field, "Unknown")
199 width = 80 - len(field) - alignment
200 lines = pprint.pformat(value, indent=2, width=width).splitlines(True)
201
202 inner = [lines[0]]
203 for line in lines[1:]:
204 inner.append(" " * alignment + line)
205 result.append(f"{field}={''.join(inner)}")
206
207 return string % {
208 "cname": cname,
209 "rname": rname,
210 "fields": (",\n" + " " * alignment).join(result),
211 }
212
213 def __repr__(self) -> str:
214 rname = self.repr_name()
215 # The dependencies used to calculate fromlineno (if not cached) may not exist at the time
216 try:
217 lineno = self.fromlineno
218 except AttributeError:
219 lineno = 0
220 if rname:
221 string = "<%(cname)s.%(rname)s l.%(lineno)s at 0x%(id)x>"
222 else:
223 string = "<%(cname)s l.%(lineno)s at 0x%(id)x>"
224 return string % {
225 "cname": type(self).__name__,
226 "rname": rname,
227 "lineno": lineno,
228 "id": id(self),
229 }
230
231 def accept(self, visitor: AsStringVisitor) -> str:
232 """Visit this node using the given visitor."""
233 func = getattr(visitor, "visit_" + self.__class__.__name__.lower())
234 return func(self)
235
236 def get_children(self) -> Iterator[NodeNG]:
237 """Get the child nodes below this node."""
238 for field in self._astroid_fields:
239 attr = getattr(self, field)
240 if attr is None:
241 continue
242 if isinstance(attr, (list, tuple)):
243 yield from attr
244 else:
245 yield attr
246 yield from ()
247
248 def last_child(self) -> NodeNG | None:
249 """An optimized version of list(get_children())[-1]."""
250 for field in self._astroid_fields[::-1]:
251 attr = getattr(self, field)
252 if not attr: # None or empty list / tuple
253 continue
254 if isinstance(attr, (list, tuple)):
255 return attr[-1]
256 return attr
257 return None
258
259 def node_ancestors(self) -> Iterator[NodeNG]:
260 """Yield parent, grandparent, etc until there are no more."""
261 parent = self.parent
262 while parent is not None:
263 yield parent
264 parent = parent.parent
265
266 def parent_of(self, node) -> bool:
267 """Check if this node is the parent of the given node.
268
269 :param node: The node to check if it is the child.
270 :type node: NodeNG
271
272 :returns: Whether this node is the parent of the given node.
273 """
274 return any(self is parent for parent in node.node_ancestors())
275
276 def statement(self) -> _base_nodes.Statement:
277 """The first parent node, including self, marked as statement node.
278
279 :raises StatementMissing: If self has no parent attribute.
280 """
281 if self.is_statement:
282 return cast("_base_nodes.Statement", self)
283 if not self.parent:
284 raise StatementMissing(target=self)
285 return self.parent.statement()
286
287 def frame(self) -> nodes.FunctionDef | nodes.Module | nodes.ClassDef | nodes.Lambda:
288 """The first parent frame node.
289
290 A frame node is a :class:`Module`, :class:`FunctionDef`,
291 :class:`ClassDef` or :class:`Lambda`.
292
293 :returns: The first parent frame node.
294 :raises ParentMissingError: If self has no parent attribute.
295 """
296 if self.parent is None:
297 raise ParentMissingError(target=self)
298 return self.parent.frame()
299
300 def scope(self) -> nodes.LocalsDictNodeNG:
301 """The first parent node defining a new scope.
302
303 These can be Module, FunctionDef, ClassDef, Lambda, or GeneratorExp nodes.
304
305 :returns: The first parent scope node.
306 """
307 if not self.parent:
308 raise ParentMissingError(target=self)
309 return self.parent.scope()
310
311 def root(self) -> nodes.Module:
312 """Return the root node of the syntax tree.
313
314 :returns: The root node.
315 """
316 if not (parent := self.parent):
317 assert isinstance(self, nodes.Module)
318 return self
319
320 while parent.parent:
321 parent = parent.parent
322 assert isinstance(parent, nodes.Module)
323 return parent
324
325 def child_sequence(self, child):
326 """Search for the sequence that contains this child.
327
328 :param child: The child node to search sequences for.
329 :type child: NodeNG
330
331 :returns: The sequence containing the given child node.
332 :rtype: iterable(NodeNG)
333
334 :raises AstroidError: If no sequence could be found that contains
335 the given child.
336 """
337 for field in self._astroid_fields:
338 node_or_sequence = getattr(self, field)
339 if node_or_sequence is child:
340 return [node_or_sequence]
341 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
342 if (
343 isinstance(node_or_sequence, (tuple, list))
344 and child in node_or_sequence
345 ):
346 return node_or_sequence
347
348 msg = "Could not find %s in %s's children"
349 raise AstroidError(msg % (repr(child), repr(self)))
350
351 def locate_child(self, child):
352 """Find the field of this node that contains the given child.
353
354 :param child: The child node to search fields for.
355 :type child: NodeNG
356
357 :returns: A tuple of the name of the field that contains the child,
358 and the sequence or node that contains the child node.
359 :rtype: tuple(str, iterable(NodeNG) or NodeNG)
360
361 :raises AstroidError: If no field could be found that contains
362 the given child.
363 """
364 for field in self._astroid_fields:
365 node_or_sequence = getattr(self, field)
366 # /!\ compiler.ast Nodes have an __iter__ walking over child nodes
367 if child is node_or_sequence:
368 return field, child
369 if (
370 isinstance(node_or_sequence, (tuple, list))
371 and child in node_or_sequence
372 ):
373 return field, node_or_sequence
374 msg = "Could not find %s in %s's children"
375 raise AstroidError(msg % (repr(child), repr(self)))
376
377 # FIXME : should we merge child_sequence and locate_child ? locate_child
378 # is only used in are_exclusive, child_sequence one time in pylint.
379
380 def next_sibling(self):
381 """The next sibling statement node.
382
383 :returns: The next sibling statement node.
384 :rtype: NodeNG or None
385 """
386 return self.parent.next_sibling()
387
388 def previous_sibling(self):
389 """The previous sibling statement.
390
391 :returns: The previous sibling statement node.
392 :rtype: NodeNG or None
393 """
394 return self.parent.previous_sibling()
395
396 # these are lazy because they're relatively expensive to compute for every
397 # single node, and they rarely get looked at
398
399 @cached_property
400 def fromlineno(self) -> int:
401 """The first line that this node appears on in the source code.
402
403 Can also return 0 if the line can not be determined.
404 """
405 if self.lineno is None:
406 return self._fixed_source_line()
407 return self.lineno
408
409 @cached_property
410 def tolineno(self) -> int:
411 """The last line that this node appears on in the source code.
412
413 Can also return 0 if the line can not be determined.
414 """
415 if self.end_lineno is not None:
416 return self.end_lineno
417 if not self._astroid_fields:
418 # can't have children
419 last_child = None
420 else:
421 last_child = self.last_child()
422 if last_child is None:
423 return self.fromlineno
424 return last_child.tolineno
425
426 def _fixed_source_line(self) -> int:
427 """Attempt to find the line that this node appears on.
428
429 We need this method since not all nodes have :attr:`lineno` set.
430 Will return 0 if the line number can not be determined.
431 """
432 line = self.lineno
433 _node = self
434 try:
435 while line is None:
436 _node = next(_node.get_children())
437 line = _node.lineno
438 except StopIteration:
439 parent = self.parent
440 while parent and line is None:
441 line = parent.lineno
442 parent = parent.parent
443 return line or 0
444
445 def block_range(self, lineno: int) -> tuple[int, int]:
446 """Get a range from the given line number to where this node ends.
447
448 :param lineno: The line number to start the range at.
449
450 :returns: The range of line numbers that this node belongs to,
451 starting at the given line number.
452 """
453 return lineno, self.tolineno
454
455 def set_local(self, name: str, stmt: NodeNG) -> None:
456 """Define that the given name is declared in the given statement node.
457
458 This definition is stored on the parent scope node.
459
460 .. seealso:: :meth:`scope`
461
462 :param name: The name that is being defined.
463
464 :param stmt: The statement that defines the given name.
465 """
466 assert self.parent
467 self.parent.set_local(name, stmt)
468
469 @overload
470 def nodes_of_class(
471 self,
472 klass: type[_NodesT],
473 skip_klass: SkipKlassT = ...,
474 ) -> Iterator[_NodesT]: ...
475
476 @overload
477 def nodes_of_class(
478 self,
479 klass: tuple[type[_NodesT], type[_NodesT2]],
480 skip_klass: SkipKlassT = ...,
481 ) -> Iterator[_NodesT] | Iterator[_NodesT2]: ...
482
483 @overload
484 def nodes_of_class(
485 self,
486 klass: tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]],
487 skip_klass: SkipKlassT = ...,
488 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]: ...
489
490 @overload
491 def nodes_of_class(
492 self,
493 klass: tuple[type[_NodesT], ...],
494 skip_klass: SkipKlassT = ...,
495 ) -> Iterator[_NodesT]: ...
496
497 def nodes_of_class( # type: ignore[misc] # mypy doesn't correctly recognize the overloads
498 self,
499 klass: (
500 type[_NodesT]
501 | tuple[type[_NodesT], type[_NodesT2]]
502 | tuple[type[_NodesT], type[_NodesT2], type[_NodesT3]]
503 | tuple[type[_NodesT], ...]
504 ),
505 skip_klass: SkipKlassT = None,
506 ) -> Iterator[_NodesT] | Iterator[_NodesT2] | Iterator[_NodesT3]:
507 """Get the nodes (including this one or below) of the given types.
508
509 :param klass: The types of node to search for.
510
511 :param skip_klass: The types of node to ignore. This is useful to ignore
512 subclasses of :attr:`klass`.
513
514 :returns: The node of the given types.
515 """
516 if isinstance(self, klass):
517 yield self
518
519 if skip_klass is None:
520 for child_node in self.get_children():
521 yield from child_node.nodes_of_class(klass, skip_klass)
522
523 return
524
525 for child_node in self.get_children():
526 if isinstance(child_node, skip_klass):
527 continue
528 yield from child_node.nodes_of_class(klass, skip_klass)
529
530 @cached_property
531 def _assign_nodes_in_scope(self) -> list[nodes.Assign]:
532 return []
533
534 def _get_name_nodes(self):
535 for child_node in self.get_children():
536 yield from child_node._get_name_nodes()
537
538 def _get_return_nodes_skip_functions(self):
539 yield from ()
540
541 def _get_yield_nodes_skip_functions(self):
542 yield from ()
543
544 def _get_yield_nodes_skip_lambdas(self):
545 yield from ()
546
547 def _infer_name(self, frame, name):
548 # overridden for ImportFrom, Import, Global, Try, TryStar and Arguments
549 pass
550
551 def _infer(
552 self, context: InferenceContext | None = None, **kwargs: Any
553 ) -> Generator[InferenceResult, None, InferenceErrorInfo | None]:
554 """We don't know how to resolve a statement by default."""
555 # this method is overridden by most concrete classes
556 raise InferenceError(
557 "No inference function for {node!r}.", node=self, context=context
558 )
559
560 def inferred(self):
561 """Get a list of the inferred values.
562
563 .. seealso:: :ref:`inference`
564
565 :returns: The inferred values.
566 :rtype: list
567 """
568 return list(self.infer())
569
570 def instantiate_class(self):
571 """Instantiate an instance of the defined class.
572
573 .. note::
574
575 On anything other than a :class:`ClassDef` this will return self.
576
577 :returns: An instance of the defined class.
578 :rtype: object
579 """
580 return self
581
582 def has_base(self, node) -> bool:
583 """Check if this node inherits from the given type.
584
585 :param node: The node defining the base to look for.
586 Usually this is a :class:`Name` node.
587 :type node: NodeNG
588 """
589 return False
590
591 def callable(self) -> bool:
592 """Whether this node defines something that is callable.
593
594 :returns: Whether this defines something that is callable.
595 """
596 return False
597
598 def eq(self, value) -> bool:
599 return False
600
601 def as_string(self) -> str:
602 """Get the source code that this node represents."""
603 return AsStringVisitor()(self)
604
605 def repr_tree(
606 self,
607 ids=False,
608 include_linenos=False,
609 ast_state=False,
610 indent=" ",
611 max_depth=0,
612 max_width=80,
613 ) -> str:
614 """Get a string representation of the AST from this node.
615
616 :param ids: If true, includes the ids with the node type names.
617 :type ids: bool
618
619 :param include_linenos: If true, includes the line numbers and
620 column offsets.
621 :type include_linenos: bool
622
623 :param ast_state: If true, includes information derived from
624 the whole AST like local and global variables.
625 :type ast_state: bool
626
627 :param indent: A string to use to indent the output string.
628 :type indent: str
629
630 :param max_depth: If set to a positive integer, won't return
631 nodes deeper than max_depth in the string.
632 :type max_depth: int
633
634 :param max_width: Attempt to format the output string to stay
635 within this number of characters, but can exceed it under some
636 circumstances. Only positive integer values are valid, the default is 80.
637 :type max_width: int
638
639 :returns: The string representation of the AST.
640 :rtype: str
641 """
642
643 # pylint: disable = too-many-statements
644
645 @_singledispatch
646 def _repr_tree(node, result, done, cur_indent="", depth=1):
647 """Outputs a representation of a non-tuple/list, non-node that's
648 contained within an AST, including strings.
649 """
650 lines = pprint.pformat(
651 node, width=max(max_width - len(cur_indent), 1)
652 ).splitlines(True)
653 result.append(lines[0])
654 result.extend([cur_indent + line for line in lines[1:]])
655 return len(lines) != 1
656
657 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
658 @_repr_tree.register(tuple)
659 @_repr_tree.register(list)
660 def _repr_seq(node, result, done, cur_indent="", depth=1):
661 """Outputs a representation of a sequence that's contained within an
662 AST.
663 """
664 cur_indent += indent
665 result.append("[")
666 if not node:
667 broken = False
668 elif len(node) == 1:
669 broken = _repr_tree(node[0], result, done, cur_indent, depth)
670 elif len(node) == 2:
671 broken = _repr_tree(node[0], result, done, cur_indent, depth)
672 if not broken:
673 result.append(", ")
674 else:
675 result.append(",\n")
676 result.append(cur_indent)
677 broken = _repr_tree(node[1], result, done, cur_indent, depth) or broken
678 else:
679 result.append("\n")
680 result.append(cur_indent)
681 for child in node[:-1]:
682 _repr_tree(child, result, done, cur_indent, depth)
683 result.append(",\n")
684 result.append(cur_indent)
685 _repr_tree(node[-1], result, done, cur_indent, depth)
686 broken = True
687 result.append("]")
688 return broken
689
690 # pylint: disable=unused-variable,useless-suppression; doesn't understand singledispatch
691 @_repr_tree.register(NodeNG)
692 def _repr_node(node, result, done, cur_indent="", depth=1):
693 """Outputs a strings representation of an astroid node."""
694 if node in done:
695 result.append(
696 indent + f"<Recursion on {type(node).__name__} with id={id(node)}"
697 )
698 return False
699 done.add(node)
700
701 if max_depth and depth > max_depth:
702 result.append("...")
703 return False
704 depth += 1
705 cur_indent += indent
706 if ids:
707 result.append(f"{type(node).__name__}<0x{id(node):x}>(\n")
708 else:
709 result.append(f"{type(node).__name__}(")
710 fields = []
711 if include_linenos:
712 fields.extend(("lineno", "col_offset"))
713 fields.extend(node._other_fields)
714 fields.extend(node._astroid_fields)
715 if ast_state:
716 fields.extend(node._other_other_fields)
717 if not fields:
718 broken = False
719 elif len(fields) == 1:
720 result.append(f"{fields[0]}=")
721 broken = _repr_tree(
722 getattr(node, fields[0]), result, done, cur_indent, depth
723 )
724 else:
725 result.append("\n")
726 result.append(cur_indent)
727 for field in fields[:-1]:
728 # TODO: Remove this after removal of the 'doc' attribute
729 if field == "doc":
730 continue
731 result.append(f"{field}=")
732 _repr_tree(getattr(node, field), result, done, cur_indent, depth)
733 result.append(",\n")
734 result.append(cur_indent)
735 result.append(f"{fields[-1]}=")
736 _repr_tree(getattr(node, fields[-1]), result, done, cur_indent, depth)
737 broken = True
738 result.append(")")
739 return broken
740
741 result: list[str] = []
742 _repr_tree(self, result, set())
743 return "".join(result)
744
745 def bool_value(self, context: InferenceContext | None = None):
746 """Determine the boolean value of this node.
747
748 The boolean value of a node can have three
749 possible values:
750
751 * False: For instance, empty data structures,
752 False, empty strings, instances which return
753 explicitly False from the __nonzero__ / __bool__
754 method.
755 * True: Most of constructs are True by default:
756 classes, functions, modules etc
757 * Uninferable: The inference engine is uncertain of the
758 node's value.
759
760 :returns: The boolean value of this node.
761 :rtype: bool or Uninferable
762 """
763 return util.Uninferable
764
765 def op_precedence(self) -> int:
766 # Look up by class name or default to highest precedence
767 return OP_PRECEDENCE.get(self.__class__.__name__, len(OP_PRECEDENCE))
768
769 def op_left_associative(self) -> bool:
770 # Everything is left associative except `**` and IfExp
771 return True