strconv.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641
  1. """Conversion of parse tree nodes to strings."""
  2. from __future__ import annotations
  3. import os
  4. import re
  5. from typing import TYPE_CHECKING, Any, Sequence
  6. import mypy.nodes
  7. from mypy.options import Options
  8. from mypy.util import IdMapper, short_type
  9. from mypy.visitor import NodeVisitor
  10. if TYPE_CHECKING:
  11. import mypy.patterns
  12. import mypy.types
  13. class StrConv(NodeVisitor[str]):
  14. """Visitor for converting a node to a human-readable string.
  15. For example, an MypyFile node from program '1' is converted into
  16. something like this:
  17. MypyFile:1(
  18. fnam
  19. ExpressionStmt:1(
  20. IntExpr(1)))
  21. """
  22. __slots__ = ["options", "show_ids", "id_mapper"]
  23. def __init__(self, *, show_ids: bool = False, options: Options) -> None:
  24. self.options = options
  25. self.show_ids = show_ids
  26. self.id_mapper: IdMapper | None = None
  27. if show_ids:
  28. self.id_mapper = IdMapper()
  29. def stringify_type(self, t: mypy.types.Type) -> str:
  30. import mypy.types
  31. return t.accept(mypy.types.TypeStrVisitor(id_mapper=self.id_mapper, options=self.options))
  32. def get_id(self, o: object) -> int | None:
  33. if self.id_mapper:
  34. return self.id_mapper.id(o)
  35. return None
  36. def format_id(self, o: object) -> str:
  37. if self.id_mapper:
  38. return f"<{self.get_id(o)}>"
  39. else:
  40. return ""
  41. def dump(self, nodes: Sequence[object], obj: mypy.nodes.Context) -> str:
  42. """Convert a list of items to a multiline pretty-printed string.
  43. The tag is produced from the type name of obj and its line
  44. number. See mypy.util.dump_tagged for a description of the nodes
  45. argument.
  46. """
  47. tag = short_type(obj) + ":" + str(obj.line)
  48. if self.show_ids:
  49. assert self.id_mapper is not None
  50. tag += f"<{self.get_id(obj)}>"
  51. return dump_tagged(nodes, tag, self)
  52. def func_helper(self, o: mypy.nodes.FuncItem) -> list[object]:
  53. """Return a list in a format suitable for dump() that represents the
  54. arguments and the body of a function. The caller can then decorate the
  55. array with information specific to methods, global functions or
  56. anonymous functions.
  57. """
  58. args: list[mypy.nodes.Var | tuple[str, list[mypy.nodes.Node]]] = []
  59. extra: list[tuple[str, list[mypy.nodes.Var]]] = []
  60. for arg in o.arguments:
  61. kind: mypy.nodes.ArgKind = arg.kind
  62. if kind.is_required():
  63. args.append(arg.variable)
  64. elif kind.is_optional():
  65. assert arg.initializer is not None
  66. args.append(("default", [arg.variable, arg.initializer]))
  67. elif kind == mypy.nodes.ARG_STAR:
  68. extra.append(("VarArg", [arg.variable]))
  69. elif kind == mypy.nodes.ARG_STAR2:
  70. extra.append(("DictVarArg", [arg.variable]))
  71. a: list[Any] = []
  72. if args:
  73. a.append(("Args", args))
  74. if o.type:
  75. a.append(o.type)
  76. if o.is_generator:
  77. a.append("Generator")
  78. a.extend(extra)
  79. a.append(o.body)
  80. return a
  81. # Top-level structures
  82. def visit_mypy_file(self, o: mypy.nodes.MypyFile) -> str:
  83. # Skip implicit definitions.
  84. a: list[Any] = [o.defs]
  85. if o.is_bom:
  86. a.insert(0, "BOM")
  87. # Omit path to special file with name "main". This is used to simplify
  88. # test case descriptions; the file "main" is used by default in many
  89. # test cases.
  90. if o.path != "main":
  91. # Insert path. Normalize directory separators to / to unify test
  92. # case# output in all platforms.
  93. a.insert(0, o.path.replace(os.sep, "/"))
  94. if o.ignored_lines:
  95. a.append("IgnoredLines(%s)" % ", ".join(str(line) for line in sorted(o.ignored_lines)))
  96. return self.dump(a, o)
  97. def visit_import(self, o: mypy.nodes.Import) -> str:
  98. a = []
  99. for id, as_id in o.ids:
  100. if as_id is not None:
  101. a.append(f"{id} : {as_id}")
  102. else:
  103. a.append(id)
  104. return f"Import:{o.line}({', '.join(a)})"
  105. def visit_import_from(self, o: mypy.nodes.ImportFrom) -> str:
  106. a = []
  107. for name, as_name in o.names:
  108. if as_name is not None:
  109. a.append(f"{name} : {as_name}")
  110. else:
  111. a.append(name)
  112. return f"ImportFrom:{o.line}({'.' * o.relative + o.id}, [{', '.join(a)}])"
  113. def visit_import_all(self, o: mypy.nodes.ImportAll) -> str:
  114. return f"ImportAll:{o.line}({'.' * o.relative + o.id})"
  115. # Definitions
  116. def visit_func_def(self, o: mypy.nodes.FuncDef) -> str:
  117. a = self.func_helper(o)
  118. a.insert(0, o.name)
  119. arg_kinds = {arg.kind for arg in o.arguments}
  120. if len(arg_kinds & {mypy.nodes.ARG_NAMED, mypy.nodes.ARG_NAMED_OPT}) > 0:
  121. a.insert(1, f"MaxPos({o.max_pos})")
  122. if o.abstract_status in (mypy.nodes.IS_ABSTRACT, mypy.nodes.IMPLICITLY_ABSTRACT):
  123. a.insert(-1, "Abstract")
  124. if o.is_static:
  125. a.insert(-1, "Static")
  126. if o.is_class:
  127. a.insert(-1, "Class")
  128. if o.is_property:
  129. a.insert(-1, "Property")
  130. return self.dump(a, o)
  131. def visit_overloaded_func_def(self, o: mypy.nodes.OverloadedFuncDef) -> str:
  132. a: Any = o.items.copy()
  133. if o.type:
  134. a.insert(0, o.type)
  135. if o.impl:
  136. a.insert(0, o.impl)
  137. if o.is_static:
  138. a.insert(-1, "Static")
  139. if o.is_class:
  140. a.insert(-1, "Class")
  141. return self.dump(a, o)
  142. def visit_class_def(self, o: mypy.nodes.ClassDef) -> str:
  143. a = [o.name, o.defs.body]
  144. # Display base types unless they are implicitly just builtins.object
  145. # (in this case base_type_exprs is empty).
  146. if o.base_type_exprs:
  147. if o.info and o.info.bases:
  148. if len(o.info.bases) != 1 or o.info.bases[0].type.fullname != "builtins.object":
  149. a.insert(1, ("BaseType", o.info.bases))
  150. else:
  151. a.insert(1, ("BaseTypeExpr", o.base_type_exprs))
  152. if o.type_vars:
  153. a.insert(1, ("TypeVars", o.type_vars))
  154. if o.metaclass:
  155. a.insert(1, f"Metaclass({o.metaclass.accept(self)})")
  156. if o.decorators:
  157. a.insert(1, ("Decorators", o.decorators))
  158. if o.info and o.info._promote:
  159. a.insert(1, f"Promote([{','.join(self.stringify_type(p) for p in o.info._promote)}])")
  160. if o.info and o.info.tuple_type:
  161. a.insert(1, ("TupleType", [o.info.tuple_type]))
  162. if o.info and o.info.fallback_to_any:
  163. a.insert(1, "FallbackToAny")
  164. return self.dump(a, o)
  165. def visit_var(self, o: mypy.nodes.Var) -> str:
  166. lst = ""
  167. # Add :nil line number tag if no line number is specified to remain
  168. # compatible with old test case descriptions that assume this.
  169. if o.line < 0:
  170. lst = ":nil"
  171. return "Var" + lst + "(" + o.name + ")"
  172. def visit_global_decl(self, o: mypy.nodes.GlobalDecl) -> str:
  173. return self.dump([o.names], o)
  174. def visit_nonlocal_decl(self, o: mypy.nodes.NonlocalDecl) -> str:
  175. return self.dump([o.names], o)
  176. def visit_decorator(self, o: mypy.nodes.Decorator) -> str:
  177. return self.dump([o.var, o.decorators, o.func], o)
  178. # Statements
  179. def visit_block(self, o: mypy.nodes.Block) -> str:
  180. return self.dump(o.body, o)
  181. def visit_expression_stmt(self, o: mypy.nodes.ExpressionStmt) -> str:
  182. return self.dump([o.expr], o)
  183. def visit_assignment_stmt(self, o: mypy.nodes.AssignmentStmt) -> str:
  184. a: list[Any] = []
  185. if len(o.lvalues) > 1:
  186. a = [("Lvalues", o.lvalues)]
  187. else:
  188. a = [o.lvalues[0]]
  189. a.append(o.rvalue)
  190. if o.type:
  191. a.append(o.type)
  192. return self.dump(a, o)
  193. def visit_operator_assignment_stmt(self, o: mypy.nodes.OperatorAssignmentStmt) -> str:
  194. return self.dump([o.op, o.lvalue, o.rvalue], o)
  195. def visit_while_stmt(self, o: mypy.nodes.WhileStmt) -> str:
  196. a: list[Any] = [o.expr, o.body]
  197. if o.else_body:
  198. a.append(("Else", o.else_body.body))
  199. return self.dump(a, o)
  200. def visit_for_stmt(self, o: mypy.nodes.ForStmt) -> str:
  201. a: list[Any] = []
  202. if o.is_async:
  203. a.append(("Async", ""))
  204. a.append(o.index)
  205. if o.index_type:
  206. a.append(o.index_type)
  207. a.extend([o.expr, o.body])
  208. if o.else_body:
  209. a.append(("Else", o.else_body.body))
  210. return self.dump(a, o)
  211. def visit_return_stmt(self, o: mypy.nodes.ReturnStmt) -> str:
  212. return self.dump([o.expr], o)
  213. def visit_if_stmt(self, o: mypy.nodes.IfStmt) -> str:
  214. a: list[Any] = []
  215. for i in range(len(o.expr)):
  216. a.append(("If", [o.expr[i]]))
  217. a.append(("Then", o.body[i].body))
  218. if not o.else_body:
  219. return self.dump(a, o)
  220. else:
  221. return self.dump([a, ("Else", o.else_body.body)], o)
  222. def visit_break_stmt(self, o: mypy.nodes.BreakStmt) -> str:
  223. return self.dump([], o)
  224. def visit_continue_stmt(self, o: mypy.nodes.ContinueStmt) -> str:
  225. return self.dump([], o)
  226. def visit_pass_stmt(self, o: mypy.nodes.PassStmt) -> str:
  227. return self.dump([], o)
  228. def visit_raise_stmt(self, o: mypy.nodes.RaiseStmt) -> str:
  229. return self.dump([o.expr, o.from_expr], o)
  230. def visit_assert_stmt(self, o: mypy.nodes.AssertStmt) -> str:
  231. if o.msg is not None:
  232. return self.dump([o.expr, o.msg], o)
  233. else:
  234. return self.dump([o.expr], o)
  235. def visit_await_expr(self, o: mypy.nodes.AwaitExpr) -> str:
  236. return self.dump([o.expr], o)
  237. def visit_del_stmt(self, o: mypy.nodes.DelStmt) -> str:
  238. return self.dump([o.expr], o)
  239. def visit_try_stmt(self, o: mypy.nodes.TryStmt) -> str:
  240. a: list[Any] = [o.body]
  241. if o.is_star:
  242. a.append("*")
  243. for i in range(len(o.vars)):
  244. a.append(o.types[i])
  245. if o.vars[i]:
  246. a.append(o.vars[i])
  247. a.append(o.handlers[i])
  248. if o.else_body:
  249. a.append(("Else", o.else_body.body))
  250. if o.finally_body:
  251. a.append(("Finally", o.finally_body.body))
  252. return self.dump(a, o)
  253. def visit_with_stmt(self, o: mypy.nodes.WithStmt) -> str:
  254. a: list[Any] = []
  255. if o.is_async:
  256. a.append(("Async", ""))
  257. for i in range(len(o.expr)):
  258. a.append(("Expr", [o.expr[i]]))
  259. if o.target[i]:
  260. a.append(("Target", [o.target[i]]))
  261. if o.unanalyzed_type:
  262. a.append(o.unanalyzed_type)
  263. return self.dump(a + [o.body], o)
  264. def visit_match_stmt(self, o: mypy.nodes.MatchStmt) -> str:
  265. a: list[Any] = [o.subject]
  266. for i in range(len(o.patterns)):
  267. a.append(("Pattern", [o.patterns[i]]))
  268. if o.guards[i] is not None:
  269. a.append(("Guard", [o.guards[i]]))
  270. a.append(("Body", o.bodies[i].body))
  271. return self.dump(a, o)
  272. # Expressions
  273. # Simple expressions
  274. def visit_int_expr(self, o: mypy.nodes.IntExpr) -> str:
  275. return f"IntExpr({o.value})"
  276. def visit_str_expr(self, o: mypy.nodes.StrExpr) -> str:
  277. return f"StrExpr({self.str_repr(o.value)})"
  278. def visit_bytes_expr(self, o: mypy.nodes.BytesExpr) -> str:
  279. return f"BytesExpr({self.str_repr(o.value)})"
  280. def str_repr(self, s: str) -> str:
  281. s = re.sub(r"\\u[0-9a-fA-F]{4}", lambda m: "\\" + m.group(0), s)
  282. return re.sub("[^\\x20-\\x7e]", lambda m: r"\u%.4x" % ord(m.group(0)), s)
  283. def visit_float_expr(self, o: mypy.nodes.FloatExpr) -> str:
  284. return f"FloatExpr({o.value})"
  285. def visit_complex_expr(self, o: mypy.nodes.ComplexExpr) -> str:
  286. return f"ComplexExpr({o.value})"
  287. def visit_ellipsis(self, o: mypy.nodes.EllipsisExpr) -> str:
  288. return "Ellipsis"
  289. def visit_star_expr(self, o: mypy.nodes.StarExpr) -> str:
  290. return self.dump([o.expr], o)
  291. def visit_name_expr(self, o: mypy.nodes.NameExpr) -> str:
  292. pretty = self.pretty_name(
  293. o.name, o.kind, o.fullname, o.is_inferred_def or o.is_special_form, o.node
  294. )
  295. if isinstance(o.node, mypy.nodes.Var) and o.node.is_final:
  296. pretty += f" = {o.node.final_value}"
  297. return short_type(o) + "(" + pretty + ")"
  298. def pretty_name(
  299. self,
  300. name: str,
  301. kind: int | None,
  302. fullname: str | None,
  303. is_inferred_def: bool,
  304. target_node: mypy.nodes.Node | None = None,
  305. ) -> str:
  306. n = name
  307. if is_inferred_def:
  308. n += "*"
  309. if target_node:
  310. id = self.format_id(target_node)
  311. else:
  312. id = ""
  313. if isinstance(target_node, mypy.nodes.MypyFile) and name == fullname:
  314. n += id
  315. elif kind == mypy.nodes.GDEF or (fullname != name and fullname):
  316. # Append fully qualified name for global references.
  317. n += f" [{fullname}{id}]"
  318. elif kind == mypy.nodes.LDEF:
  319. # Add tag to signify a local reference.
  320. n += f" [l{id}]"
  321. elif kind == mypy.nodes.MDEF:
  322. # Add tag to signify a member reference.
  323. n += f" [m{id}]"
  324. else:
  325. n += id
  326. return n
  327. def visit_member_expr(self, o: mypy.nodes.MemberExpr) -> str:
  328. pretty = self.pretty_name(o.name, o.kind, o.fullname, o.is_inferred_def, o.node)
  329. return self.dump([o.expr, pretty], o)
  330. def visit_yield_expr(self, o: mypy.nodes.YieldExpr) -> str:
  331. return self.dump([o.expr], o)
  332. def visit_yield_from_expr(self, o: mypy.nodes.YieldFromExpr) -> str:
  333. if o.expr:
  334. return self.dump([o.expr.accept(self)], o)
  335. else:
  336. return self.dump([], o)
  337. def visit_call_expr(self, o: mypy.nodes.CallExpr) -> str:
  338. if o.analyzed:
  339. return o.analyzed.accept(self)
  340. args: list[mypy.nodes.Expression] = []
  341. extra: list[str | tuple[str, list[Any]]] = []
  342. for i, kind in enumerate(o.arg_kinds):
  343. if kind in [mypy.nodes.ARG_POS, mypy.nodes.ARG_STAR]:
  344. args.append(o.args[i])
  345. if kind == mypy.nodes.ARG_STAR:
  346. extra.append("VarArg")
  347. elif kind == mypy.nodes.ARG_NAMED:
  348. extra.append(("KwArgs", [o.arg_names[i], o.args[i]]))
  349. elif kind == mypy.nodes.ARG_STAR2:
  350. extra.append(("DictVarArg", [o.args[i]]))
  351. else:
  352. raise RuntimeError(f"unknown kind {kind}")
  353. a: list[Any] = [o.callee, ("Args", args)]
  354. return self.dump(a + extra, o)
  355. def visit_op_expr(self, o: mypy.nodes.OpExpr) -> str:
  356. if o.analyzed:
  357. return o.analyzed.accept(self)
  358. return self.dump([o.op, o.left, o.right], o)
  359. def visit_comparison_expr(self, o: mypy.nodes.ComparisonExpr) -> str:
  360. return self.dump([o.operators, o.operands], o)
  361. def visit_cast_expr(self, o: mypy.nodes.CastExpr) -> str:
  362. return self.dump([o.expr, o.type], o)
  363. def visit_assert_type_expr(self, o: mypy.nodes.AssertTypeExpr) -> str:
  364. return self.dump([o.expr, o.type], o)
  365. def visit_reveal_expr(self, o: mypy.nodes.RevealExpr) -> str:
  366. if o.kind == mypy.nodes.REVEAL_TYPE:
  367. return self.dump([o.expr], o)
  368. else:
  369. # REVEAL_LOCALS
  370. return self.dump([o.local_nodes], o)
  371. def visit_assignment_expr(self, o: mypy.nodes.AssignmentExpr) -> str:
  372. return self.dump([o.target, o.value], o)
  373. def visit_unary_expr(self, o: mypy.nodes.UnaryExpr) -> str:
  374. return self.dump([o.op, o.expr], o)
  375. def visit_list_expr(self, o: mypy.nodes.ListExpr) -> str:
  376. return self.dump(o.items, o)
  377. def visit_dict_expr(self, o: mypy.nodes.DictExpr) -> str:
  378. return self.dump([[k, v] for k, v in o.items], o)
  379. def visit_set_expr(self, o: mypy.nodes.SetExpr) -> str:
  380. return self.dump(o.items, o)
  381. def visit_tuple_expr(self, o: mypy.nodes.TupleExpr) -> str:
  382. return self.dump(o.items, o)
  383. def visit_index_expr(self, o: mypy.nodes.IndexExpr) -> str:
  384. if o.analyzed:
  385. return o.analyzed.accept(self)
  386. return self.dump([o.base, o.index], o)
  387. def visit_super_expr(self, o: mypy.nodes.SuperExpr) -> str:
  388. return self.dump([o.name, o.call], o)
  389. def visit_type_application(self, o: mypy.nodes.TypeApplication) -> str:
  390. return self.dump([o.expr, ("Types", o.types)], o)
  391. def visit_type_var_expr(self, o: mypy.nodes.TypeVarExpr) -> str:
  392. import mypy.types
  393. a: list[Any] = []
  394. if o.variance == mypy.nodes.COVARIANT:
  395. a += ["Variance(COVARIANT)"]
  396. if o.variance == mypy.nodes.CONTRAVARIANT:
  397. a += ["Variance(CONTRAVARIANT)"]
  398. if o.values:
  399. a += [("Values", o.values)]
  400. if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"):
  401. a += [f"UpperBound({self.stringify_type(o.upper_bound)})"]
  402. return self.dump(a, o)
  403. def visit_paramspec_expr(self, o: mypy.nodes.ParamSpecExpr) -> str:
  404. import mypy.types
  405. a: list[Any] = []
  406. if o.variance == mypy.nodes.COVARIANT:
  407. a += ["Variance(COVARIANT)"]
  408. if o.variance == mypy.nodes.CONTRAVARIANT:
  409. a += ["Variance(CONTRAVARIANT)"]
  410. if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"):
  411. a += [f"UpperBound({self.stringify_type(o.upper_bound)})"]
  412. return self.dump(a, o)
  413. def visit_type_var_tuple_expr(self, o: mypy.nodes.TypeVarTupleExpr) -> str:
  414. import mypy.types
  415. a: list[Any] = []
  416. if o.variance == mypy.nodes.COVARIANT:
  417. a += ["Variance(COVARIANT)"]
  418. if o.variance == mypy.nodes.CONTRAVARIANT:
  419. a += ["Variance(CONTRAVARIANT)"]
  420. if not mypy.types.is_named_instance(o.upper_bound, "builtins.object"):
  421. a += [f"UpperBound({self.stringify_type(o.upper_bound)})"]
  422. return self.dump(a, o)
  423. def visit_type_alias_expr(self, o: mypy.nodes.TypeAliasExpr) -> str:
  424. return f"TypeAliasExpr({self.stringify_type(o.type)})"
  425. def visit_namedtuple_expr(self, o: mypy.nodes.NamedTupleExpr) -> str:
  426. return f"NamedTupleExpr:{o.line}({o.info.name}, {self.stringify_type(o.info.tuple_type) if o.info.tuple_type is not None else None})"
  427. def visit_enum_call_expr(self, o: mypy.nodes.EnumCallExpr) -> str:
  428. return f"EnumCallExpr:{o.line}({o.info.name}, {o.items})"
  429. def visit_typeddict_expr(self, o: mypy.nodes.TypedDictExpr) -> str:
  430. return f"TypedDictExpr:{o.line}({o.info.name})"
  431. def visit__promote_expr(self, o: mypy.nodes.PromoteExpr) -> str:
  432. return f"PromoteExpr:{o.line}({self.stringify_type(o.type)})"
  433. def visit_newtype_expr(self, o: mypy.nodes.NewTypeExpr) -> str:
  434. return f"NewTypeExpr:{o.line}({o.name}, {self.dump([o.old_type], o)})"
  435. def visit_lambda_expr(self, o: mypy.nodes.LambdaExpr) -> str:
  436. a = self.func_helper(o)
  437. return self.dump(a, o)
  438. def visit_generator_expr(self, o: mypy.nodes.GeneratorExpr) -> str:
  439. condlists = o.condlists if any(o.condlists) else None
  440. return self.dump([o.left_expr, o.indices, o.sequences, condlists], o)
  441. def visit_list_comprehension(self, o: mypy.nodes.ListComprehension) -> str:
  442. return self.dump([o.generator], o)
  443. def visit_set_comprehension(self, o: mypy.nodes.SetComprehension) -> str:
  444. return self.dump([o.generator], o)
  445. def visit_dictionary_comprehension(self, o: mypy.nodes.DictionaryComprehension) -> str:
  446. condlists = o.condlists if any(o.condlists) else None
  447. return self.dump([o.key, o.value, o.indices, o.sequences, condlists], o)
  448. def visit_conditional_expr(self, o: mypy.nodes.ConditionalExpr) -> str:
  449. return self.dump([("Condition", [o.cond]), o.if_expr, o.else_expr], o)
  450. def visit_slice_expr(self, o: mypy.nodes.SliceExpr) -> str:
  451. a: list[Any] = [o.begin_index, o.end_index, o.stride]
  452. if not a[0]:
  453. a[0] = "<empty>"
  454. if not a[1]:
  455. a[1] = "<empty>"
  456. return self.dump(a, o)
  457. def visit_temp_node(self, o: mypy.nodes.TempNode) -> str:
  458. return self.dump([o.type], o)
  459. def visit_as_pattern(self, o: mypy.patterns.AsPattern) -> str:
  460. return self.dump([o.pattern, o.name], o)
  461. def visit_or_pattern(self, o: mypy.patterns.OrPattern) -> str:
  462. return self.dump(o.patterns, o)
  463. def visit_value_pattern(self, o: mypy.patterns.ValuePattern) -> str:
  464. return self.dump([o.expr], o)
  465. def visit_singleton_pattern(self, o: mypy.patterns.SingletonPattern) -> str:
  466. return self.dump([o.value], o)
  467. def visit_sequence_pattern(self, o: mypy.patterns.SequencePattern) -> str:
  468. return self.dump(o.patterns, o)
  469. def visit_starred_pattern(self, o: mypy.patterns.StarredPattern) -> str:
  470. return self.dump([o.capture], o)
  471. def visit_mapping_pattern(self, o: mypy.patterns.MappingPattern) -> str:
  472. a: list[Any] = []
  473. for i in range(len(o.keys)):
  474. a.append(("Key", [o.keys[i]]))
  475. a.append(("Value", [o.values[i]]))
  476. if o.rest is not None:
  477. a.append(("Rest", [o.rest]))
  478. return self.dump(a, o)
  479. def visit_class_pattern(self, o: mypy.patterns.ClassPattern) -> str:
  480. a: list[Any] = [o.class_ref]
  481. if len(o.positionals) > 0:
  482. a.append(("Positionals", o.positionals))
  483. for i in range(len(o.keyword_keys)):
  484. a.append(("Keyword", [o.keyword_keys[i], o.keyword_values[i]]))
  485. return self.dump(a, o)
  486. def dump_tagged(nodes: Sequence[object], tag: str | None, str_conv: StrConv) -> str:
  487. """Convert an array into a pretty-printed multiline string representation.
  488. The format is
  489. tag(
  490. item1..
  491. itemN)
  492. Individual items are formatted like this:
  493. - arrays are flattened
  494. - pairs (str, array) are converted recursively, so that str is the tag
  495. - other items are converted to strings and indented
  496. """
  497. from mypy.types import Type, TypeStrVisitor
  498. a: list[str] = []
  499. if tag:
  500. a.append(tag + "(")
  501. for n in nodes:
  502. if isinstance(n, list):
  503. if n:
  504. a.append(dump_tagged(n, None, str_conv))
  505. elif isinstance(n, tuple):
  506. s = dump_tagged(n[1], n[0], str_conv)
  507. a.append(indent(s, 2))
  508. elif isinstance(n, mypy.nodes.Node):
  509. a.append(indent(n.accept(str_conv), 2))
  510. elif isinstance(n, Type):
  511. a.append(
  512. indent(n.accept(TypeStrVisitor(str_conv.id_mapper, options=str_conv.options)), 2)
  513. )
  514. elif n is not None:
  515. a.append(indent(str(n), 2))
  516. if tag:
  517. a[-1] += ")"
  518. return "\n".join(a)
  519. def indent(s: str, n: int) -> str:
  520. """Indent all the lines in s (separated by newlines) by n spaces."""
  521. s = " " * n + s
  522. s = s.replace("\n", "\n" + " " * n)
  523. return s