rebuilder.py 78 KB

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