@tsonic/emitter 0.0.62 → 0.0.64

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (520) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-generator.d.ts +5 -15
  3. package/dist/adapter-generator.d.ts.map +1 -1
  4. package/dist/adapter-generator.js +60 -53
  5. package/dist/adapter-generator.js.map +1 -1
  6. package/dist/contracts/backend-adapter.d.ts +36 -0
  7. package/dist/contracts/backend-adapter.d.ts.map +1 -0
  8. package/dist/contracts/backend-adapter.js +9 -0
  9. package/dist/contracts/backend-adapter.js.map +1 -0
  10. package/dist/contracts/emitter-contract.d.ts +54 -0
  11. package/dist/contracts/emitter-contract.d.ts.map +1 -0
  12. package/dist/contracts/emitter-contract.js +8 -0
  13. package/dist/contracts/emitter-contract.js.map +1 -0
  14. package/dist/contracts/import-classifier.d.ts +36 -0
  15. package/dist/contracts/import-classifier.d.ts.map +1 -0
  16. package/dist/contracts/import-classifier.js +9 -0
  17. package/dist/contracts/import-classifier.js.map +1 -0
  18. package/dist/core/{attributes.d.ts → format/attributes.d.ts} +9 -14
  19. package/dist/core/format/attributes.d.ts.map +1 -0
  20. package/dist/core/format/attributes.js +190 -0
  21. package/dist/core/format/attributes.js.map +1 -0
  22. package/dist/core/format/attributes.test.d.ts.map +1 -0
  23. package/dist/core/{attributes.test.js → format/attributes.test.js} +70 -32
  24. package/dist/core/format/attributes.test.js.map +1 -0
  25. package/dist/core/format/backend-ast/index.d.ts +7 -0
  26. package/dist/core/format/backend-ast/index.d.ts.map +1 -0
  27. package/dist/core/format/backend-ast/index.js +6 -0
  28. package/dist/core/format/backend-ast/index.js.map +1 -0
  29. package/dist/core/format/backend-ast/printer.d.ts +33 -0
  30. package/dist/core/format/backend-ast/printer.d.ts.map +1 -0
  31. package/dist/core/format/backend-ast/printer.js +1151 -0
  32. package/dist/core/format/backend-ast/printer.js.map +1 -0
  33. package/dist/core/format/backend-ast/types.d.ts +504 -0
  34. package/dist/core/format/backend-ast/types.d.ts.map +1 -0
  35. package/dist/core/format/backend-ast/types.js +13 -0
  36. package/dist/core/format/backend-ast/types.js.map +1 -0
  37. package/dist/core/format/backend-ast/utils.d.ts +26 -0
  38. package/dist/core/format/backend-ast/utils.d.ts.map +1 -0
  39. package/dist/core/format/backend-ast/utils.js +65 -0
  40. package/dist/core/format/backend-ast/utils.js.map +1 -0
  41. package/dist/core/{exports.d.ts → format/exports.d.ts} +2 -2
  42. package/dist/core/format/exports.d.ts.map +1 -0
  43. package/dist/core/format/exports.js +22 -0
  44. package/dist/core/format/exports.js.map +1 -0
  45. package/dist/core/format/index.d.ts +9 -0
  46. package/dist/core/format/index.d.ts.map +1 -0
  47. package/dist/core/format/index.js +9 -0
  48. package/dist/core/format/index.js.map +1 -0
  49. package/dist/core/{local-names.d.ts → format/local-names.d.ts} +1 -1
  50. package/dist/core/format/local-names.d.ts.map +1 -0
  51. package/dist/core/{local-names.js → format/local-names.js} +1 -1
  52. package/dist/core/format/local-names.js.map +1 -0
  53. package/dist/core/format/module-emitter/assembly.d.ts +27 -0
  54. package/dist/core/format/module-emitter/assembly.d.ts.map +1 -0
  55. package/dist/core/format/module-emitter/assembly.js +44 -0
  56. package/dist/core/format/module-emitter/assembly.js.map +1 -0
  57. package/dist/core/{module-emitter → format/module-emitter}/header.d.ts +1 -1
  58. package/dist/core/format/module-emitter/header.d.ts.map +1 -0
  59. package/dist/core/{module-emitter → format/module-emitter}/header.js +1 -1
  60. package/dist/core/format/module-emitter/header.js.map +1 -0
  61. package/dist/core/format/module-emitter/index.d.ts.map +1 -0
  62. package/dist/core/format/module-emitter/index.js.map +1 -0
  63. package/dist/core/format/module-emitter/namespace.d.ts +21 -0
  64. package/dist/core/format/module-emitter/namespace.d.ts.map +1 -0
  65. package/dist/core/format/module-emitter/namespace.js +58 -0
  66. package/dist/core/format/module-emitter/namespace.js.map +1 -0
  67. package/dist/core/{module-emitter → format/module-emitter}/orchestrator.d.ts +1 -1
  68. package/dist/core/format/module-emitter/orchestrator.d.ts.map +1 -0
  69. package/dist/core/{module-emitter → format/module-emitter}/orchestrator.js +19 -19
  70. package/dist/core/format/module-emitter/orchestrator.js.map +1 -0
  71. package/dist/core/format/module-emitter/separation.d.ts.map +1 -0
  72. package/dist/core/format/module-emitter/separation.js.map +1 -0
  73. package/dist/core/{module-emitter → format/module-emitter}/static-container.d.ts +8 -3
  74. package/dist/core/format/module-emitter/static-container.d.ts.map +1 -0
  75. package/dist/core/format/module-emitter/static-container.js +202 -0
  76. package/dist/core/format/module-emitter/static-container.js.map +1 -0
  77. package/dist/core/format/module-emitter.d.ts.map +1 -0
  78. package/dist/core/format/module-emitter.js.map +1 -0
  79. package/dist/core/{options.d.ts → format/options.d.ts} +1 -1
  80. package/dist/core/format/options.d.ts.map +1 -0
  81. package/dist/core/format/options.js.map +1 -0
  82. package/dist/core/index.d.ts +2 -7
  83. package/dist/core/index.d.ts.map +1 -1
  84. package/dist/core/index.js +2 -7
  85. package/dist/core/index.js.map +1 -1
  86. package/dist/core/semantic/boolean-context.d.ts +44 -0
  87. package/dist/core/semantic/boolean-context.d.ts.map +1 -0
  88. package/dist/core/semantic/boolean-context.js +717 -0
  89. package/dist/core/semantic/boolean-context.js.map +1 -0
  90. package/dist/core/semantic/boolean-context.test.d.ts.map +1 -0
  91. package/dist/core/{boolean-context.test.js → semantic/boolean-context.test.js} +86 -109
  92. package/dist/core/semantic/boolean-context.test.js.map +1 -0
  93. package/dist/core/{imports.d.ts → semantic/imports.d.ts} +1 -5
  94. package/dist/core/semantic/imports.d.ts.map +1 -0
  95. package/dist/core/{imports.js → semantic/imports.js} +56 -74
  96. package/dist/core/semantic/imports.js.map +1 -0
  97. package/dist/core/semantic/imports.test.d.ts.map +1 -0
  98. package/dist/core/semantic/imports.test.js +229 -0
  99. package/dist/core/semantic/imports.test.js.map +1 -0
  100. package/dist/core/semantic/index.d.ts +15 -0
  101. package/dist/core/semantic/index.d.ts.map +1 -0
  102. package/dist/core/semantic/index.js +15 -0
  103. package/dist/core/semantic/index.js.map +1 -0
  104. package/dist/core/{local-types.d.ts → semantic/local-types.d.ts} +1 -1
  105. package/dist/core/semantic/local-types.d.ts.map +1 -0
  106. package/dist/core/semantic/local-types.js.map +1 -0
  107. package/dist/core/{module-map.d.ts → semantic/module-map.d.ts} +1 -1
  108. package/dist/core/semantic/module-map.d.ts.map +1 -0
  109. package/dist/core/semantic/module-map.js.map +1 -0
  110. package/dist/core/semantic/module-map.test.d.ts.map +1 -0
  111. package/dist/core/semantic/module-map.test.js.map +1 -0
  112. package/dist/core/semantic/naming-collisions.d.ts.map +1 -0
  113. package/dist/core/{naming-collisions.js → semantic/naming-collisions.js} +1 -1
  114. package/dist/core/semantic/naming-collisions.js.map +1 -0
  115. package/dist/core/{type-alias-index.d.ts → semantic/type-alias-index.d.ts} +1 -1
  116. package/dist/core/semantic/type-alias-index.d.ts.map +1 -0
  117. package/dist/core/semantic/type-alias-index.js.map +1 -0
  118. package/dist/core/semantic/type-compatibility.d.ts.map +1 -0
  119. package/dist/core/semantic/type-compatibility.js.map +1 -0
  120. package/dist/core/{type-member-index.d.ts → semantic/type-member-index.d.ts} +1 -1
  121. package/dist/core/semantic/type-member-index.d.ts.map +1 -0
  122. package/dist/core/semantic/type-member-index.js.map +1 -0
  123. package/dist/core/semantic/type-params.d.ts.map +1 -0
  124. package/dist/core/semantic/type-params.js.map +1 -0
  125. package/dist/core/{type-resolution.d.ts → semantic/type-resolution.d.ts} +3 -6
  126. package/dist/core/semantic/type-resolution.d.ts.map +1 -0
  127. package/dist/core/{type-resolution.js → semantic/type-resolution.js} +15 -25
  128. package/dist/core/semantic/type-resolution.js.map +1 -0
  129. package/dist/core/semantic/type-resolution.test.d.ts.map +1 -0
  130. package/dist/core/{type-resolution.test.js → semantic/type-resolution.test.js} +11 -19
  131. package/dist/core/semantic/type-resolution.test.js.map +1 -0
  132. package/dist/core/semantic/unsafe.d.ts.map +1 -0
  133. package/dist/core/semantic/unsafe.js.map +1 -0
  134. package/dist/emitter-types/core.d.ts +6 -12
  135. package/dist/emitter-types/core.d.ts.map +1 -1
  136. package/dist/emitter-types/index.d.ts +1 -1
  137. package/dist/emitter-types/index.d.ts.map +1 -1
  138. package/dist/emitter-types/index.js.map +1 -1
  139. package/dist/emitter.d.ts +1 -1
  140. package/dist/emitter.d.ts.map +1 -1
  141. package/dist/emitter.js +9 -8
  142. package/dist/emitter.js.map +1 -1
  143. package/dist/expression-emitter.d.ts +10 -5
  144. package/dist/expression-emitter.d.ts.map +1 -1
  145. package/dist/expression-emitter.js +333 -117
  146. package/dist/expression-emitter.js.map +1 -1
  147. package/dist/expressions/access.d.ts +4 -3
  148. package/dist/expressions/access.d.ts.map +1 -1
  149. package/dist/expressions/access.js +252 -102
  150. package/dist/expressions/access.js.map +1 -1
  151. package/dist/expressions/calls/call-analysis.d.ts +86 -0
  152. package/dist/expressions/calls/call-analysis.d.ts.map +1 -0
  153. package/dist/expressions/calls/call-analysis.js +284 -0
  154. package/dist/expressions/calls/call-analysis.js.map +1 -0
  155. package/dist/expressions/calls/call-emitter.d.ts +13 -0
  156. package/dist/expressions/calls/call-emitter.d.ts.map +1 -0
  157. package/dist/expressions/calls/call-emitter.js +1048 -0
  158. package/dist/expressions/calls/call-emitter.js.map +1 -0
  159. package/dist/expressions/calls/new-emitter.d.ts +13 -0
  160. package/dist/expressions/calls/new-emitter.d.ts.map +1 -0
  161. package/dist/expressions/calls/new-emitter.js +641 -0
  162. package/dist/expressions/calls/new-emitter.js.map +1 -0
  163. package/dist/expressions/calls.d.ts +2 -14
  164. package/dist/expressions/calls.d.ts.map +1 -1
  165. package/dist/expressions/calls.js +2 -909
  166. package/dist/expressions/calls.js.map +1 -1
  167. package/dist/expressions/collections.d.ts +6 -16
  168. package/dist/expressions/collections.d.ts.map +1 -1
  169. package/dist/expressions/collections.js +318 -217
  170. package/dist/expressions/collections.js.map +1 -1
  171. package/dist/expressions/functions.d.ts +6 -5
  172. package/dist/expressions/functions.d.ts.map +1 -1
  173. package/dist/expressions/functions.js +57 -62
  174. package/dist/expressions/functions.js.map +1 -1
  175. package/dist/expressions/identifiers.d.ts +11 -6
  176. package/dist/expressions/identifiers.d.ts.map +1 -1
  177. package/dist/expressions/identifiers.js +76 -25
  178. package/dist/expressions/identifiers.js.map +1 -1
  179. package/dist/expressions/index.d.ts +1 -1
  180. package/dist/expressions/index.d.ts.map +1 -1
  181. package/dist/expressions/index.js +1 -1
  182. package/dist/expressions/index.js.map +1 -1
  183. package/dist/expressions/index.test.js +605 -0
  184. package/dist/expressions/index.test.js.map +1 -1
  185. package/dist/expressions/literals.d.ts +4 -3
  186. package/dist/expressions/literals.d.ts.map +1 -1
  187. package/dist/expressions/literals.js +25 -17
  188. package/dist/expressions/literals.js.map +1 -1
  189. package/dist/expressions/literals.test.js +18 -18
  190. package/dist/expressions/literals.test.js.map +1 -1
  191. package/dist/expressions/operators/assignment-emitter.d.ts +16 -0
  192. package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -0
  193. package/dist/expressions/operators/assignment-emitter.js +118 -0
  194. package/dist/expressions/operators/assignment-emitter.js.map +1 -0
  195. package/dist/expressions/operators/binary-emitter.d.ts +33 -0
  196. package/dist/expressions/operators/binary-emitter.d.ts.map +1 -0
  197. package/dist/expressions/operators/binary-emitter.js +398 -0
  198. package/dist/expressions/operators/binary-emitter.js.map +1 -0
  199. package/dist/expressions/operators/conditional-emitter.d.ts +17 -0
  200. package/dist/expressions/operators/conditional-emitter.d.ts.map +1 -0
  201. package/dist/expressions/operators/conditional-emitter.js +306 -0
  202. package/dist/expressions/operators/conditional-emitter.js.map +1 -0
  203. package/dist/expressions/operators/helpers.d.ts +37 -0
  204. package/dist/expressions/operators/helpers.d.ts.map +1 -0
  205. package/dist/expressions/operators/helpers.js +136 -0
  206. package/dist/expressions/operators/helpers.js.map +1 -0
  207. package/dist/expressions/operators/logical-emitter.d.ts +23 -0
  208. package/dist/expressions/operators/logical-emitter.d.ts.map +1 -0
  209. package/dist/expressions/operators/logical-emitter.js +73 -0
  210. package/dist/expressions/operators/logical-emitter.js.map +1 -0
  211. package/dist/expressions/operators/unary-emitter.d.ts +30 -0
  212. package/dist/expressions/operators/unary-emitter.d.ts.map +1 -0
  213. package/dist/expressions/operators/unary-emitter.js +244 -0
  214. package/dist/expressions/operators/unary-emitter.js.map +1 -0
  215. package/dist/expressions/operators.d.ts +5 -81
  216. package/dist/expressions/operators.d.ts.map +1 -1
  217. package/dist/expressions/operators.js +5 -949
  218. package/dist/expressions/operators.js.map +1 -1
  219. package/dist/expressions/other.d.ts +15 -11
  220. package/dist/expressions/other.d.ts.map +1 -1
  221. package/dist/expressions/other.js +51 -36
  222. package/dist/expressions/other.js.map +1 -1
  223. package/dist/expressions/precedence.test.js +1 -1
  224. package/dist/expressions/precedence.test.js.map +1 -1
  225. package/dist/generator-exchange.d.ts +10 -3
  226. package/dist/generator-exchange.d.ts.map +1 -1
  227. package/dist/generator-exchange.js +57 -54
  228. package/dist/generator-exchange.js.map +1 -1
  229. package/dist/generator-wrapper.d.ts +17 -65
  230. package/dist/generator-wrapper.d.ts.map +1 -1
  231. package/dist/generator-wrapper.js +396 -220
  232. package/dist/generator-wrapper.js.map +1 -1
  233. package/dist/generator-wrapper.test.js +22 -14
  234. package/dist/generator-wrapper.test.js.map +1 -1
  235. package/dist/integration.test.js +169 -1
  236. package/dist/integration.test.js.map +1 -1
  237. package/dist/patterns.d.ts +18 -88
  238. package/dist/patterns.d.ts.map +1 -1
  239. package/dist/patterns.js +540 -304
  240. package/dist/patterns.js.map +1 -1
  241. package/dist/patterns.test.js +5 -4
  242. package/dist/patterns.test.js.map +1 -1
  243. package/dist/specialization/generation.d.ts +7 -3
  244. package/dist/specialization/generation.d.ts.map +1 -1
  245. package/dist/specialization/generation.js +31 -15
  246. package/dist/specialization/generation.js.map +1 -1
  247. package/dist/specialization/type-aliases.test.js +47 -2
  248. package/dist/specialization/type-aliases.test.js.map +1 -1
  249. package/dist/statement-emitter.d.ts +15 -4
  250. package/dist/statement-emitter.d.ts.map +1 -1
  251. package/dist/statement-emitter.js +54 -47
  252. package/dist/statement-emitter.js.map +1 -1
  253. package/dist/statements/blocks.d.ts +24 -16
  254. package/dist/statements/blocks.d.ts.map +1 -1
  255. package/dist/statements/blocks.js +242 -65
  256. package/dist/statements/blocks.js.map +1 -1
  257. package/dist/statements/classes/index.d.ts +1 -1
  258. package/dist/statements/classes/index.d.ts.map +1 -1
  259. package/dist/statements/classes/index.js +1 -1
  260. package/dist/statements/classes/index.js.map +1 -1
  261. package/dist/statements/classes/inline-types.d.ts +4 -3
  262. package/dist/statements/classes/inline-types.d.ts.map +1 -1
  263. package/dist/statements/classes/inline-types.js +21 -21
  264. package/dist/statements/classes/inline-types.js.map +1 -1
  265. package/dist/statements/classes/members/constructors.d.ts +4 -3
  266. package/dist/statements/classes/members/constructors.d.ts.map +1 -1
  267. package/dist/statements/classes/members/constructors.js +57 -58
  268. package/dist/statements/classes/members/constructors.js.map +1 -1
  269. package/dist/statements/classes/members/methods.d.ts +4 -3
  270. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  271. package/dist/statements/classes/members/methods.js +106 -101
  272. package/dist/statements/classes/members/methods.js.map +1 -1
  273. package/dist/statements/classes/members/orchestrator.d.ts +4 -3
  274. package/dist/statements/classes/members/orchestrator.d.ts.map +1 -1
  275. package/dist/statements/classes/members/orchestrator.js +2 -2
  276. package/dist/statements/classes/members/orchestrator.js.map +1 -1
  277. package/dist/statements/classes/members/properties.d.ts +4 -3
  278. package/dist/statements/classes/members/properties.d.ts.map +1 -1
  279. package/dist/statements/classes/members/properties.js +105 -88
  280. package/dist/statements/classes/members/properties.js.map +1 -1
  281. package/dist/statements/classes/members/shadowing.test.js +7 -2
  282. package/dist/statements/classes/members/shadowing.test.js.map +1 -1
  283. package/dist/statements/classes/members/static-readonly-properties.test.js +4 -1
  284. package/dist/statements/classes/members/static-readonly-properties.test.js.map +1 -1
  285. package/dist/statements/classes/parameters.d.ts +8 -10
  286. package/dist/statements/classes/parameters.d.ts.map +1 -1
  287. package/dist/statements/classes/parameters.js +30 -26
  288. package/dist/statements/classes/parameters.js.map +1 -1
  289. package/dist/statements/classes/properties.d.ts +4 -3
  290. package/dist/statements/classes/properties.d.ts.map +1 -1
  291. package/dist/statements/classes/properties.js +76 -50
  292. package/dist/statements/classes/properties.js.map +1 -1
  293. package/dist/statements/classes.d.ts +1 -1
  294. package/dist/statements/classes.d.ts.map +1 -1
  295. package/dist/statements/classes.js +1 -1
  296. package/dist/statements/classes.js.map +1 -1
  297. package/dist/statements/control/conditionals/guard-analysis.d.ts +169 -0
  298. package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -0
  299. package/dist/statements/control/conditionals/guard-analysis.js +591 -0
  300. package/dist/statements/control/conditionals/guard-analysis.js.map +1 -0
  301. package/dist/statements/control/conditionals/if-emitter.d.ts +14 -0
  302. package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -0
  303. package/dist/statements/control/conditionals/if-emitter.js +725 -0
  304. package/dist/statements/control/conditionals/if-emitter.js.map +1 -0
  305. package/dist/statements/control/conditionals/switch-emitter.d.ts +13 -0
  306. package/dist/statements/control/conditionals/switch-emitter.d.ts.map +1 -0
  307. package/dist/statements/control/conditionals/switch-emitter.js +60 -0
  308. package/dist/statements/control/conditionals/switch-emitter.js.map +1 -0
  309. package/dist/statements/control/conditionals.d.ts +3 -15
  310. package/dist/statements/control/conditionals.d.ts.map +1 -1
  311. package/dist/statements/control/conditionals.js +3 -1152
  312. package/dist/statements/control/conditionals.js.map +1 -1
  313. package/dist/statements/control/exceptions.d.ts +8 -6
  314. package/dist/statements/control/exceptions.d.ts.map +1 -1
  315. package/dist/statements/control/exceptions.js +35 -23
  316. package/dist/statements/control/exceptions.js.map +1 -1
  317. package/dist/statements/control/index.d.ts +3 -3
  318. package/dist/statements/control/index.d.ts.map +1 -1
  319. package/dist/statements/control/index.js +3 -3
  320. package/dist/statements/control/index.js.map +1 -1
  321. package/dist/statements/control/loops.d.ts +14 -12
  322. package/dist/statements/control/loops.d.ts.map +1 -1
  323. package/dist/statements/control/loops.js +147 -82
  324. package/dist/statements/control/loops.js.map +1 -1
  325. package/dist/statements/control.d.ts +1 -1
  326. package/dist/statements/control.d.ts.map +1 -1
  327. package/dist/statements/control.js +1 -1
  328. package/dist/statements/control.js.map +1 -1
  329. package/dist/statements/declarations/classes.d.ts +7 -3
  330. package/dist/statements/declarations/classes.d.ts.map +1 -1
  331. package/dist/statements/declarations/classes.js +107 -83
  332. package/dist/statements/declarations/classes.js.map +1 -1
  333. package/dist/statements/declarations/enums.d.ts +4 -3
  334. package/dist/statements/declarations/enums.d.ts.map +1 -1
  335. package/dist/statements/declarations/enums.js +16 -15
  336. package/dist/statements/declarations/enums.js.map +1 -1
  337. package/dist/statements/declarations/functions.d.ts +18 -2
  338. package/dist/statements/declarations/functions.d.ts.map +1 -1
  339. package/dist/statements/declarations/functions.js +633 -162
  340. package/dist/statements/declarations/functions.js.map +1 -1
  341. package/dist/statements/declarations/index.d.ts +2 -2
  342. package/dist/statements/declarations/index.d.ts.map +1 -1
  343. package/dist/statements/declarations/index.js +2 -2
  344. package/dist/statements/declarations/index.js.map +1 -1
  345. package/dist/statements/declarations/interfaces.d.ts +7 -3
  346. package/dist/statements/declarations/interfaces.d.ts.map +1 -1
  347. package/dist/statements/declarations/interfaces.js +138 -105
  348. package/dist/statements/declarations/interfaces.js.map +1 -1
  349. package/dist/statements/declarations/type-aliases.d.ts +7 -3
  350. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  351. package/dist/statements/declarations/type-aliases.js +122 -82
  352. package/dist/statements/declarations/type-aliases.js.map +1 -1
  353. package/dist/statements/declarations/variables.d.ts +12 -2
  354. package/dist/statements/declarations/variables.d.ts.map +1 -1
  355. package/dist/statements/declarations/variables.js +541 -428
  356. package/dist/statements/declarations/variables.js.map +1 -1
  357. package/dist/statements/declarations.d.ts +1 -1
  358. package/dist/statements/declarations.d.ts.map +1 -1
  359. package/dist/statements/declarations.js +1 -1
  360. package/dist/statements/declarations.js.map +1 -1
  361. package/dist/statements/index.d.ts +3 -3
  362. package/dist/statements/index.d.ts.map +1 -1
  363. package/dist/statements/index.js +5 -5
  364. package/dist/statements/index.js.map +1 -1
  365. package/dist/statements/index.test.js +307 -0
  366. package/dist/statements/index.test.js.map +1 -1
  367. package/dist/type-emitter.d.ts +1 -1
  368. package/dist/type-emitter.d.ts.map +1 -1
  369. package/dist/type-emitter.js +1 -1
  370. package/dist/type-emitter.js.map +1 -1
  371. package/dist/types/arrays.d.ts +3 -2
  372. package/dist/types/arrays.d.ts.map +1 -1
  373. package/dist/types/arrays.js +7 -5
  374. package/dist/types/arrays.js.map +1 -1
  375. package/dist/types/dictionaries.d.ts +6 -3
  376. package/dist/types/dictionaries.d.ts.map +1 -1
  377. package/dist/types/dictionaries.js +22 -10
  378. package/dist/types/dictionaries.js.map +1 -1
  379. package/dist/types/emitter.d.ts +8 -2
  380. package/dist/types/emitter.d.ts.map +1 -1
  381. package/dist/types/emitter.js +20 -6
  382. package/dist/types/emitter.js.map +1 -1
  383. package/dist/types/functions.d.ts +3 -2
  384. package/dist/types/functions.d.ts.map +1 -1
  385. package/dist/types/functions.js +36 -13
  386. package/dist/types/functions.js.map +1 -1
  387. package/dist/types/index.d.ts +2 -2
  388. package/dist/types/index.d.ts.map +1 -1
  389. package/dist/types/index.js +2 -2
  390. package/dist/types/index.js.map +1 -1
  391. package/dist/types/index.test.js +137 -0
  392. package/dist/types/index.test.js.map +1 -1
  393. package/dist/types/intersections.d.ts +3 -2
  394. package/dist/types/intersections.d.ts.map +1 -1
  395. package/dist/types/intersections.js +2 -2
  396. package/dist/types/intersections.js.map +1 -1
  397. package/dist/types/literals.d.ts +9 -2
  398. package/dist/types/literals.d.ts.map +1 -1
  399. package/dist/types/literals.js +11 -5
  400. package/dist/types/literals.js.map +1 -1
  401. package/dist/types/objects.d.ts +3 -2
  402. package/dist/types/objects.d.ts.map +1 -1
  403. package/dist/types/objects.js +1 -1
  404. package/dist/types/objects.js.map +1 -1
  405. package/dist/types/parameters.d.ts +6 -5
  406. package/dist/types/parameters.d.ts.map +1 -1
  407. package/dist/types/parameters.js +88 -29
  408. package/dist/types/parameters.js.map +1 -1
  409. package/dist/types/parameters.test.js +9 -4
  410. package/dist/types/parameters.test.js.map +1 -1
  411. package/dist/types/primitives.d.ts +5 -4
  412. package/dist/types/primitives.d.ts.map +1 -1
  413. package/dist/types/primitives.js +14 -13
  414. package/dist/types/primitives.js.map +1 -1
  415. package/dist/types/references.d.ts +3 -2
  416. package/dist/types/references.d.ts.map +1 -1
  417. package/dist/types/references.js +135 -111
  418. package/dist/types/references.js.map +1 -1
  419. package/dist/types/tuples.d.ts +3 -2
  420. package/dist/types/tuples.d.ts.map +1 -1
  421. package/dist/types/tuples.js +25 -11
  422. package/dist/types/tuples.js.map +1 -1
  423. package/dist/types/unions.d.ts +3 -2
  424. package/dist/types/unions.d.ts.map +1 -1
  425. package/dist/types/unions.js +36 -21
  426. package/dist/types/unions.js.map +1 -1
  427. package/dist/types.d.ts +1 -1
  428. package/dist/types.d.ts.map +1 -1
  429. package/dist/types.js.map +1 -1
  430. package/package.json +2 -2
  431. package/dist/core/attributes.d.ts.map +0 -1
  432. package/dist/core/attributes.js +0 -141
  433. package/dist/core/attributes.js.map +0 -1
  434. package/dist/core/attributes.test.d.ts.map +0 -1
  435. package/dist/core/attributes.test.js.map +0 -1
  436. package/dist/core/boolean-context.d.ts +0 -42
  437. package/dist/core/boolean-context.d.ts.map +0 -1
  438. package/dist/core/boolean-context.js +0 -442
  439. package/dist/core/boolean-context.js.map +0 -1
  440. package/dist/core/boolean-context.test.d.ts.map +0 -1
  441. package/dist/core/boolean-context.test.js.map +0 -1
  442. package/dist/core/exports.d.ts.map +0 -1
  443. package/dist/core/exports.js +0 -28
  444. package/dist/core/exports.js.map +0 -1
  445. package/dist/core/imports.d.ts.map +0 -1
  446. package/dist/core/imports.js.map +0 -1
  447. package/dist/core/imports.test.d.ts.map +0 -1
  448. package/dist/core/imports.test.js +0 -79
  449. package/dist/core/imports.test.js.map +0 -1
  450. package/dist/core/local-names.d.ts.map +0 -1
  451. package/dist/core/local-names.js.map +0 -1
  452. package/dist/core/local-types.d.ts.map +0 -1
  453. package/dist/core/local-types.js.map +0 -1
  454. package/dist/core/module-emitter/assembly.d.ts +0 -24
  455. package/dist/core/module-emitter/assembly.d.ts.map +0 -1
  456. package/dist/core/module-emitter/assembly.js +0 -69
  457. package/dist/core/module-emitter/assembly.js.map +0 -1
  458. package/dist/core/module-emitter/header.d.ts.map +0 -1
  459. package/dist/core/module-emitter/header.js.map +0 -1
  460. package/dist/core/module-emitter/index.d.ts.map +0 -1
  461. package/dist/core/module-emitter/index.js.map +0 -1
  462. package/dist/core/module-emitter/namespace.d.ts +0 -14
  463. package/dist/core/module-emitter/namespace.d.ts.map +0 -1
  464. package/dist/core/module-emitter/namespace.js +0 -26
  465. package/dist/core/module-emitter/namespace.js.map +0 -1
  466. package/dist/core/module-emitter/orchestrator.d.ts.map +0 -1
  467. package/dist/core/module-emitter/orchestrator.js.map +0 -1
  468. package/dist/core/module-emitter/separation.d.ts.map +0 -1
  469. package/dist/core/module-emitter/separation.js.map +0 -1
  470. package/dist/core/module-emitter/static-container.d.ts.map +0 -1
  471. package/dist/core/module-emitter/static-container.js +0 -139
  472. package/dist/core/module-emitter/static-container.js.map +0 -1
  473. package/dist/core/module-emitter.d.ts.map +0 -1
  474. package/dist/core/module-emitter.js.map +0 -1
  475. package/dist/core/module-map.d.ts.map +0 -1
  476. package/dist/core/module-map.js.map +0 -1
  477. package/dist/core/module-map.test.d.ts.map +0 -1
  478. package/dist/core/module-map.test.js.map +0 -1
  479. package/dist/core/naming-collisions.d.ts.map +0 -1
  480. package/dist/core/naming-collisions.js.map +0 -1
  481. package/dist/core/options.d.ts.map +0 -1
  482. package/dist/core/options.js.map +0 -1
  483. package/dist/core/type-alias-index.d.ts.map +0 -1
  484. package/dist/core/type-alias-index.js.map +0 -1
  485. package/dist/core/type-compatibility.d.ts.map +0 -1
  486. package/dist/core/type-compatibility.js.map +0 -1
  487. package/dist/core/type-member-index.d.ts.map +0 -1
  488. package/dist/core/type-member-index.js.map +0 -1
  489. package/dist/core/type-params.d.ts.map +0 -1
  490. package/dist/core/type-params.js.map +0 -1
  491. package/dist/core/type-resolution.d.ts.map +0 -1
  492. package/dist/core/type-resolution.js.map +0 -1
  493. package/dist/core/type-resolution.test.d.ts.map +0 -1
  494. package/dist/core/type-resolution.test.js.map +0 -1
  495. package/dist/core/unsafe.d.ts.map +0 -1
  496. package/dist/core/unsafe.js.map +0 -1
  497. /package/dist/core/{attributes.test.d.ts → format/attributes.test.d.ts} +0 -0
  498. /package/dist/core/{module-emitter → format/module-emitter}/index.d.ts +0 -0
  499. /package/dist/core/{module-emitter → format/module-emitter}/index.js +0 -0
  500. /package/dist/core/{module-emitter → format/module-emitter}/separation.d.ts +0 -0
  501. /package/dist/core/{module-emitter → format/module-emitter}/separation.js +0 -0
  502. /package/dist/core/{module-emitter.d.ts → format/module-emitter.d.ts} +0 -0
  503. /package/dist/core/{module-emitter.js → format/module-emitter.js} +0 -0
  504. /package/dist/core/{options.js → format/options.js} +0 -0
  505. /package/dist/core/{boolean-context.test.d.ts → semantic/boolean-context.test.d.ts} +0 -0
  506. /package/dist/core/{imports.test.d.ts → semantic/imports.test.d.ts} +0 -0
  507. /package/dist/core/{local-types.js → semantic/local-types.js} +0 -0
  508. /package/dist/core/{module-map.js → semantic/module-map.js} +0 -0
  509. /package/dist/core/{module-map.test.d.ts → semantic/module-map.test.d.ts} +0 -0
  510. /package/dist/core/{module-map.test.js → semantic/module-map.test.js} +0 -0
  511. /package/dist/core/{naming-collisions.d.ts → semantic/naming-collisions.d.ts} +0 -0
  512. /package/dist/core/{type-alias-index.js → semantic/type-alias-index.js} +0 -0
  513. /package/dist/core/{type-compatibility.d.ts → semantic/type-compatibility.d.ts} +0 -0
  514. /package/dist/core/{type-compatibility.js → semantic/type-compatibility.js} +0 -0
  515. /package/dist/core/{type-member-index.js → semantic/type-member-index.js} +0 -0
  516. /package/dist/core/{type-params.d.ts → semantic/type-params.d.ts} +0 -0
  517. /package/dist/core/{type-params.js → semantic/type-params.js} +0 -0
  518. /package/dist/core/{type-resolution.test.d.ts → semantic/type-resolution.test.d.ts} +0 -0
  519. /package/dist/core/{unsafe.d.ts → semantic/unsafe.d.ts} +0 -0
  520. /package/dist/core/{unsafe.js → semantic/unsafe.js} +0 -0
@@ -1,1155 +1,6 @@
1
1
  /**
2
- * Conditional statement emitters (if, switch)
2
+ * Conditional statement emitters (if, switch) - barrel re-export.
3
3
  */
4
- import { getIndent, indent, dedent, } from "../../types.js";
5
- import { emitExpression } from "../../expression-emitter.js";
6
- import { emitIdentifier } from "../../expressions/identifiers.js";
7
- import { emitStatement } from "../../statement-emitter.js";
8
- import { resolveTypeAlias, stripNullish, findUnionMemberIndex, getPropertyType, getAllPropertySignatures, isDefinitelyValueType, } from "../../core/type-resolution.js";
9
- import { escapeCSharpIdentifier } from "../../emitter-types/index.js";
10
- import { emitBooleanCondition, toBooleanCondition, } from "../../core/boolean-context.js";
11
- import { emitRemappedLocalName } from "../../core/local-names.js";
12
- /**
13
- * Check if a local nominal type (class/interface) has a property with the given TS name.
14
- */
15
- const hasLocalProperty = (type, propertyName, context) => {
16
- if (!context.localTypes)
17
- return false;
18
- const info = context.localTypes.get(type.name);
19
- if (!info)
20
- return false;
21
- if (info.kind === "interface") {
22
- const props = getAllPropertySignatures(type, context);
23
- return props?.some((p) => p.name === propertyName) ?? false;
24
- }
25
- if (info.kind === "class") {
26
- return info.members.some((m) => m.kind === "propertyDeclaration" && m.name === propertyName);
27
- }
28
- return false;
29
- };
30
- /**
31
- * Check if a nominal type has a property, including cross-module local types.
32
- *
33
- * For same-module types, consult `context.localTypes`.
34
- * For cross-module types, consult the batch `typeMemberIndex` and resolve the
35
- * member's fully-qualified name deterministically.
36
- */
37
- const hasProperty = (type, propertyName, context) => {
38
- if (hasLocalProperty(type, propertyName, context)) {
39
- return true;
40
- }
41
- const index = context.options.typeMemberIndex;
42
- if (!index)
43
- return false;
44
- const stripGlobalPrefix = (name) => name.startsWith("global::") ? name.slice("global::".length) : name;
45
- const candidates = [];
46
- if (type.resolvedClrType) {
47
- candidates.push(stripGlobalPrefix(type.resolvedClrType));
48
- }
49
- else if (type.name.includes(".")) {
50
- candidates.push(type.name);
51
- }
52
- else {
53
- // Resolve by suffix match in the type member index.
54
- const matches = [];
55
- for (const fqn of index.keys()) {
56
- if (fqn.endsWith(`.${type.name}`) ||
57
- fqn.endsWith(`.${type.name}__Alias`)) {
58
- matches.push(fqn);
59
- }
60
- }
61
- if (matches.length === 1) {
62
- candidates.push(matches[0]);
63
- }
64
- else if (matches.length > 1) {
65
- const list = matches.sort().join(", ");
66
- throw new Error(`ICE: Ambiguous union member type '${type.name}' for \`in\` narrowing. Candidates: ${list}`);
67
- }
68
- }
69
- return candidates.some((fqn) => {
70
- const perType = index.get(fqn);
71
- return perType?.has(propertyName) ?? false;
72
- });
73
- };
74
- /**
75
- * Resolve a reference type's LocalTypeInfo map (possibly from a different module).
76
- *
77
- * This is required for airplane-grade narrowing features that depend on member *types*
78
- * (not just member names), e.g. discriminant literal equality checks.
79
- */
80
- const resolveLocalTypesForReference = (type, context) => {
81
- const lookupName = type.name.includes(".")
82
- ? (type.name.split(".").pop() ?? type.name)
83
- : type.name;
84
- if (context.localTypes?.has(lookupName)) {
85
- return context.localTypes;
86
- }
87
- const moduleMap = context.options.moduleMap;
88
- if (!moduleMap)
89
- return undefined;
90
- const matches = [];
91
- for (const m of moduleMap.values()) {
92
- if (!m.localTypes)
93
- continue;
94
- if (m.localTypes.has(lookupName)) {
95
- matches.push({
96
- namespace: m.namespace,
97
- localTypes: m.localTypes,
98
- });
99
- }
100
- }
101
- if (matches.length === 0)
102
- return undefined;
103
- if (matches.length === 1)
104
- return matches[0].localTypes;
105
- // Disambiguate by CLR FQN when available.
106
- const fqn = type.resolvedClrType ?? (type.name.includes(".") ? type.name : undefined);
107
- if (fqn && fqn.includes(".")) {
108
- const lastDot = fqn.lastIndexOf(".");
109
- const ns = fqn.slice(0, lastDot);
110
- const filtered = matches.filter((m) => m.namespace === ns);
111
- if (filtered.length === 1)
112
- return filtered[0].localTypes;
113
- }
114
- // Ambiguous: refuse to guess.
115
- return undefined;
116
- };
117
- /**
118
- * Extract the set of allowed discriminant literal values from a type.
119
- *
120
- * Airplane-grade rule:
121
- * - The discriminant property must be typed as a literal or a union of literals.
122
- * - If it includes any non-literal members (including null/undefined), we refuse to treat
123
- * it as a discriminant for equality-guard narrowing.
124
- */
125
- const tryGetLiteralSet = (type, context) => {
126
- const resolved = resolveTypeAlias(type, context);
127
- if (resolved.kind === "literalType") {
128
- return new Set([resolved.value]);
129
- }
130
- if (resolved.kind === "unionType") {
131
- const out = new Set();
132
- for (const t of resolved.types) {
133
- const r = resolveTypeAlias(t, context);
134
- if (r.kind !== "literalType")
135
- return undefined;
136
- out.add(r.value);
137
- }
138
- return out;
139
- }
140
- return undefined;
141
- };
142
- /**
143
- * Try to extract guard info from `x.prop === <literal>` or `x.prop !== <literal>`.
144
- *
145
- * This supports airplane-grade discriminated union narrowing without relying on
146
- * TypeScript flow analysis, by mapping the literal to exactly one union member.
147
- */
148
- const tryResolveDiscriminantEqualityGuard = (condition, context) => {
149
- // Normalize `!(x.prop === lit)` to `x.prop !== lit` (and vice versa).
150
- if (condition.kind === "unary" && condition.operator === "!") {
151
- const inner = tryResolveDiscriminantEqualityGuard(condition.expression, context);
152
- if (!inner)
153
- return undefined;
154
- const flipped = inner.operator === "==="
155
- ? "!=="
156
- : inner.operator === "!=="
157
- ? "==="
158
- : inner.operator === "=="
159
- ? "!="
160
- : inner.operator === "!="
161
- ? "=="
162
- : inner.operator;
163
- return { ...inner, operator: flipped };
164
- }
165
- if (condition.kind !== "binary")
166
- return undefined;
167
- if (condition.operator !== "===" &&
168
- condition.operator !== "!==" &&
169
- condition.operator !== "==" &&
170
- condition.operator !== "!=") {
171
- return undefined;
172
- }
173
- const extract = (left, right) => {
174
- if (left.kind !== "memberAccess")
175
- return undefined;
176
- if (left.isOptional)
177
- return undefined;
178
- if (left.isComputed)
179
- return undefined;
180
- if (left.object.kind !== "identifier")
181
- return undefined;
182
- if (typeof left.property !== "string")
183
- return undefined;
184
- if (right.kind !== "literal")
185
- return undefined;
186
- if (typeof right.value !== "string" &&
187
- typeof right.value !== "number" &&
188
- typeof right.value !== "boolean") {
189
- return undefined;
190
- }
191
- return {
192
- receiver: left.object,
193
- propertyName: left.property,
194
- literal: right.value,
195
- };
196
- };
197
- const direct = extract(condition.left, condition.right);
198
- const swapped = direct ? undefined : extract(condition.right, condition.left);
199
- const match = direct ?? swapped;
200
- if (!match)
201
- return undefined;
202
- const { receiver, propertyName, literal } = match;
203
- const originalName = receiver.name;
204
- // If this identifier is already narrowed (union guard emitted earlier), do NOT try to
205
- // apply another union narrowing rule. This avoids mis-emitting `.IsN()` on a narrowed member type.
206
- if (context.narrowedBindings?.has(originalName))
207
- return undefined;
208
- const unionSourceType = receiver.inferredType;
209
- if (!unionSourceType)
210
- return undefined;
211
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
212
- if (resolved.kind !== "unionType")
213
- return undefined;
214
- const unionArity = resolved.types.length;
215
- if (unionArity < 2 || unionArity > 8)
216
- return undefined;
217
- // Find which union members have a discriminant property type that includes the literal.
218
- const matchingMembers = [];
219
- for (let i = 0; i < resolved.types.length; i++) {
220
- const member = resolved.types[i];
221
- if (!member)
222
- continue;
223
- let propType;
224
- if (member.kind === "objectType") {
225
- const prop = member.members.find((m) => m.kind === "propertySignature" && m.name === propertyName);
226
- propType = prop?.type;
227
- }
228
- else if (member.kind === "referenceType") {
229
- const localTypes = resolveLocalTypesForReference(member, context);
230
- if (!localTypes)
231
- continue;
232
- const lookupName = member.name.includes(".")
233
- ? (member.name.split(".").pop() ?? member.name)
234
- : member.name;
235
- // Use the target module's localTypes for property type resolution.
236
- propType = getPropertyType({ ...member, name: lookupName }, propertyName, { ...context, localTypes });
237
- }
238
- else {
239
- continue;
240
- }
241
- if (!propType)
242
- continue;
243
- const literals = tryGetLiteralSet(propType, context);
244
- if (!literals)
245
- continue;
246
- if (literals.has(literal)) {
247
- matchingMembers.push(i + 1);
248
- }
249
- }
250
- // Only support the common airplane-grade case: exactly one matching member.
251
- if (matchingMembers.length !== 1)
252
- return undefined;
253
- const memberN = matchingMembers[0];
254
- if (!memberN)
255
- return undefined;
256
- const nextId = (context.tempVarId ?? 0) + 1;
257
- const ctxWithId = { ...context, tempVarId: nextId };
258
- const narrowedName = `${originalName}__${memberN}_${nextId}`;
259
- const escapedOrig = emitRemappedLocalName(originalName, context);
260
- const escapedNarrow = escapeCSharpIdentifier(narrowedName);
261
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
262
- const memberType = resolved.types[memberN - 1];
263
- narrowedMap.set(originalName, {
264
- kind: "rename",
265
- name: narrowedName,
266
- type: memberType,
267
- });
268
- return {
269
- originalName,
270
- propertyName,
271
- literal,
272
- operator: condition.operator,
273
- memberN,
274
- unionArity,
275
- ctxWithId,
276
- narrowedName,
277
- escapedOrig,
278
- escapedNarrow,
279
- narrowedMap,
280
- };
281
- };
282
- /**
283
- * Try to extract guard info from an `("prop" in x)` binary expression.
284
- */
285
- const tryResolveInGuard = (condition, context) => {
286
- if (condition.kind !== "binary")
287
- return undefined;
288
- if (condition.operator !== "in")
289
- return undefined;
290
- // LHS must be a string literal
291
- if (condition.left.kind !== "literal")
292
- return undefined;
293
- if (typeof condition.left.value !== "string")
294
- return undefined;
295
- // RHS must be an identifier (the name we can narrow)
296
- if (condition.right.kind !== "identifier")
297
- return undefined;
298
- const propertyName = condition.left.value;
299
- const originalName = condition.right.name;
300
- const unionSourceType = condition.right.inferredType;
301
- if (!unionSourceType)
302
- return undefined;
303
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
304
- if (resolved.kind !== "unionType")
305
- return undefined;
306
- const unionArity = resolved.types.length;
307
- if (unionArity < 2 || unionArity > 8)
308
- return undefined;
309
- // Find which union members contain the property.
310
- const matchingMembers = [];
311
- for (let i = 0; i < resolved.types.length; i++) {
312
- const member = resolved.types[i];
313
- if (!member || member.kind !== "referenceType")
314
- continue;
315
- if (hasProperty(member, propertyName, context)) {
316
- matchingMembers.push(i + 1);
317
- }
318
- }
319
- // Only support the common "exactly one matching member" narrowing case.
320
- if (matchingMembers.length !== 1)
321
- return undefined;
322
- const memberN = matchingMembers[0];
323
- if (!memberN)
324
- return undefined;
325
- const nextId = (context.tempVarId ?? 0) + 1;
326
- const ctxWithId = { ...context, tempVarId: nextId };
327
- const narrowedName = `${originalName}__${memberN}_${nextId}`;
328
- const [rhsFrag] = emitIdentifier(condition.right, context);
329
- const escapedOrig = rhsFrag.text;
330
- const escapedNarrow = escapeCSharpIdentifier(narrowedName);
331
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
332
- const memberType = resolved.types[memberN - 1];
333
- narrowedMap.set(originalName, {
334
- kind: "rename",
335
- name: narrowedName,
336
- type: memberType,
337
- });
338
- return {
339
- originalName,
340
- propertyName,
341
- memberN,
342
- unionArity,
343
- ctxWithId,
344
- narrowedName,
345
- escapedOrig,
346
- escapedNarrow,
347
- narrowedMap,
348
- };
349
- };
350
- /**
351
- * Conservative check: does a statement definitely terminate control flow?
352
- * Used to apply post-if narrowing in patterns like:
353
- * if (guard(x)) return ...;
354
- * // x is now narrowed in the remainder of the block (2-member unions only)
355
- */
356
- const isDefinitelyTerminating = (stmt) => {
357
- if (stmt.kind === "returnStatement" || stmt.kind === "throwStatement") {
358
- return true;
359
- }
360
- if (stmt.kind === "blockStatement") {
361
- const last = stmt.statements[stmt.statements.length - 1];
362
- return last ? isDefinitelyTerminating(last) : false;
363
- }
364
- return false;
365
- };
366
- /**
367
- * Try to extract guard info from a predicate call expression.
368
- * Returns GuardInfo if:
369
- * - call.narrowing is typePredicate
370
- * - predicate arg is identifier
371
- * - arg.inferredType resolves to unionType
372
- * - targetType exists in union
373
- */
374
- const tryResolvePredicateGuard = (call, context) => {
375
- const narrowing = call.narrowing;
376
- if (!narrowing || narrowing.kind !== "typePredicate")
377
- return undefined;
378
- const arg = call.arguments[narrowing.argIndex];
379
- if (!arg ||
380
- ("kind" in arg && arg.kind === "spread") ||
381
- arg.kind !== "identifier") {
382
- return undefined;
383
- }
384
- const originalName = arg.name;
385
- const unionSourceType = arg.inferredType;
386
- if (!unionSourceType)
387
- return undefined;
388
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
389
- if (resolved.kind !== "unionType")
390
- return undefined;
391
- const idx = findUnionMemberIndex(resolved, narrowing.targetType, context);
392
- if (idx === undefined)
393
- return undefined;
394
- const memberN = idx + 1;
395
- const unionArity = resolved.types.length;
396
- const nextId = (context.tempVarId ?? 0) + 1;
397
- const ctxWithId = { ...context, tempVarId: nextId };
398
- const narrowedName = `${originalName}__${memberN}_${nextId}`;
399
- const [argFrag] = emitIdentifier(arg, context);
400
- const escapedOrig = argFrag.text;
401
- const escapedNarrow = escapeCSharpIdentifier(narrowedName);
402
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
403
- narrowedMap.set(originalName, {
404
- kind: "rename",
405
- name: narrowedName,
406
- type: narrowing.targetType,
407
- });
408
- return {
409
- originalName,
410
- targetType: narrowing.targetType,
411
- memberN,
412
- unionArity,
413
- ctxWithId,
414
- narrowedName,
415
- escapedOrig,
416
- escapedNarrow,
417
- narrowedMap,
418
- };
419
- };
420
- /**
421
- * Try to extract guard info from an `instanceof` binary expression.
422
- * Returns guard info if:
423
- * - condition is `binary` with operator `instanceof`
424
- * - lhs is identifier
425
- *
426
- * Note: rhs is emitted as a type name (C# pattern).
427
- */
428
- const tryResolveInstanceofGuard = (condition, context) => {
429
- if (condition.kind !== "binary")
430
- return undefined;
431
- if (condition.operator !== "instanceof")
432
- return undefined;
433
- if (condition.left.kind !== "identifier")
434
- return undefined;
435
- const originalName = condition.left.name;
436
- const [lhsFrag, ctxAfterLhs] = emitIdentifier(condition.left, context);
437
- const escapedOrig = lhsFrag.text;
438
- const nextId = (ctxAfterLhs.tempVarId ?? 0) + 1;
439
- const ctxWithId = { ...ctxAfterLhs, tempVarId: nextId };
440
- // Emit RHS as a type name (e.g., global::System.String)
441
- const [rhsFrag, ctxAfterRhs] = emitExpression(condition.right, ctxWithId);
442
- const rhsTypeText = rhsFrag.text;
443
- // Pattern variable name for the narrowed value.
444
- const narrowedName = `${originalName}__is_${nextId}`;
445
- const escapedNarrow = escapeCSharpIdentifier(narrowedName);
446
- const narrowedMap = new Map(ctxAfterRhs.narrowedBindings ?? []);
447
- narrowedMap.set(originalName, {
448
- kind: "rename",
449
- name: narrowedName,
450
- type: condition.right.inferredType ?? undefined,
451
- });
452
- return {
453
- originalName,
454
- rhsTypeText,
455
- ctxWithId,
456
- ctxAfterRhs,
457
- narrowedName,
458
- escapedOrig,
459
- escapedNarrow,
460
- narrowedMap,
461
- targetType: condition.right.inferredType ?? undefined,
462
- };
463
- };
464
- /**
465
- * Check if an expression represents null or undefined.
466
- * Handles both literal form (from null/undefined keyword) and identifier form
467
- * (when TypeScript parses "undefined" as an identifier rather than keyword).
468
- */
469
- const isNullOrUndefined = (expr) => {
470
- // Literal form: null or undefined keyword
471
- if (expr.kind === "literal" &&
472
- (expr.value === null || expr.value === undefined)) {
473
- return true;
474
- }
475
- // Identifier form: the identifier "undefined"
476
- // (TypeScript sometimes parses undefined as identifier)
477
- if (expr.kind === "identifier" && expr.name === "undefined") {
478
- return true;
479
- }
480
- return false;
481
- };
482
- const getMemberAccessNarrowKey = (expr) => {
483
- if (expr.isComputed)
484
- return undefined;
485
- if (typeof expr.property !== "string")
486
- return undefined;
487
- const obj = expr.object;
488
- if (obj.kind === "identifier") {
489
- return `${obj.name}.${expr.property}`;
490
- }
491
- if (obj.kind === "memberAccess") {
492
- const prefix = getMemberAccessNarrowKey(obj);
493
- return prefix ? `${prefix}.${expr.property}` : undefined;
494
- }
495
- if (obj.kind === "this") {
496
- return `this.${expr.property}`;
497
- }
498
- return undefined;
499
- };
500
- /**
501
- * Try to extract nullable guard info from a simple comparison expression.
502
- * This is the core check for patterns like: id !== undefined, id !== null, id != null
503
- */
504
- const tryResolveSimpleNullableGuard = (condition) => {
505
- if (condition.kind !== "binary")
506
- return undefined;
507
- const op = condition.operator;
508
- const isNotEqual = op === "!==" || op === "!=";
509
- const isEqual = op === "===" || op === "==";
510
- if (!isNotEqual && !isEqual)
511
- return undefined;
512
- // Find operand (identifier or member access) and null/undefined expression
513
- let operand;
514
- let key;
515
- if (isNullOrUndefined(condition.right) &&
516
- (condition.left.kind === "identifier" ||
517
- condition.left.kind === "memberAccess")) {
518
- operand = condition.left;
519
- }
520
- else if (isNullOrUndefined(condition.left) &&
521
- (condition.right.kind === "identifier" ||
522
- condition.right.kind === "memberAccess")) {
523
- operand = condition.right;
524
- }
525
- if (!operand)
526
- return undefined;
527
- if (operand.kind === "identifier") {
528
- key = operand.name;
529
- }
530
- else {
531
- key = getMemberAccessNarrowKey(operand);
532
- }
533
- if (!key)
534
- return undefined;
535
- const idType = operand.inferredType;
536
- if (!idType)
537
- return undefined;
538
- // Check if type is nullable (has null or undefined in union)
539
- const stripped = stripNullish(idType);
540
- if (stripped === idType)
541
- return undefined; // Not a nullable type
542
- // Check if it's a value type that needs .Value
543
- const isValueType = isDefinitelyValueType(stripped);
544
- return {
545
- key,
546
- targetExpr: operand,
547
- strippedType: stripped,
548
- narrowsInThen: isNotEqual,
549
- isValueType,
550
- };
551
- };
552
- /**
553
- * Try to extract nullable guard info from a condition.
554
- * Detects patterns like: id !== undefined, id !== null, id != null
555
- * Also searches inside && (logical AND) conditions recursively.
556
- *
557
- * For compound conditions like `method === "GET" && id !== undefined`,
558
- * we search both sides of the && for a nullable guard pattern.
559
- *
560
- * Returns guard info if the condition is a null/undefined check on an identifier
561
- * with a nullable type that is a value type (needs .Value in C#).
562
- */
563
- const tryResolveNullableGuard = (condition, _context) => {
564
- // First try the simple case
565
- const simple = tryResolveSimpleNullableGuard(condition);
566
- if (simple)
567
- return simple;
568
- // If this is a && logical expression, search inside it
569
- if (condition.kind === "logical" && condition.operator === "&&") {
570
- // Check left side
571
- const leftGuard = tryResolveNullableGuard(condition.left, _context);
572
- if (leftGuard)
573
- return leftGuard;
574
- // Check right side
575
- const rightGuard = tryResolveNullableGuard(condition.right, _context);
576
- if (rightGuard)
577
- return rightGuard;
578
- }
579
- return undefined;
580
- };
581
- /**
582
- * Emit a forced block with a preamble line (e.g., var narrowed = x.AsN()).
583
- * If bodyStmt is already a block, emits its statements directly to avoid nesting.
584
- */
585
- const emitForcedBlockWithPreamble = (preambleLine, bodyStmt, bodyCtx, outerInd) => {
586
- const parts = [preambleLine];
587
- const emitBodyStatements = (statements, ctx) => {
588
- let currentCtx = ctx;
589
- for (const s of statements) {
590
- const [code, next] = emitStatement(s, currentCtx);
591
- parts.push(code);
592
- currentCtx = next;
593
- }
594
- return currentCtx;
595
- };
596
- const finalCtx = bodyStmt.kind === "blockStatement"
597
- ? emitBodyStatements(bodyStmt.statements, bodyCtx)
598
- : (() => {
599
- const [code, next] = emitStatement(bodyStmt, bodyCtx);
600
- parts.push(code);
601
- return next;
602
- })();
603
- const blockBody = parts.join("\n");
604
- const code = `${outerInd}{\n${blockBody}\n${outerInd}}`;
605
- return [code, finalCtx];
606
- };
607
- /**
608
- * Emit an if statement
609
- */
610
- export const emitIfStatement = (stmt, context) => {
611
- const ind = getIndent(context);
612
- // Case A: if (isUser(account)) { ... }
613
- // Predicate narrowing rewrite → if (account.IsN()) { var account__N_k = account.AsN(); ... }
614
- if (stmt.condition.kind === "call") {
615
- const guard = tryResolvePredicateGuard(stmt.condition, context);
616
- if (guard) {
617
- const { memberN, ctxWithId, escapedOrig, escapedNarrow, narrowedMap } = guard;
618
- const condText = `${escapedOrig}.Is${memberN}()`;
619
- const thenCtx = {
620
- ...indent(ctxWithId),
621
- narrowedBindings: narrowedMap,
622
- };
623
- const thenInd = getIndent(thenCtx);
624
- const castLine = `${thenInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
625
- const [thenCode, thenBodyCtx] = emitForcedBlockWithPreamble(castLine, stmt.thenStatement, thenCtx, ind);
626
- let finalContext = dedent(thenBodyCtx);
627
- finalContext = {
628
- ...finalContext,
629
- narrowedBindings: ctxWithId.narrowedBindings,
630
- };
631
- let code = `${ind}if (${condText})\n${thenCode}`;
632
- if (stmt.elseStatement) {
633
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent(finalContext));
634
- code += `\n${ind}else\n${elseCode}`;
635
- finalContext = dedent(elseCtx);
636
- }
637
- return [code, finalContext];
638
- }
639
- }
640
- // Case A3: if ("error" in auth) { ... }
641
- // Union 'in' narrowing rewrite → if (auth.IsN()) { var auth__N_k = auth.AsN(); ... }
642
- const inGuard = tryResolveInGuard(stmt.condition, context);
643
- if (inGuard) {
644
- const { originalName, memberN, unionArity, ctxWithId, escapedOrig, escapedNarrow, narrowedMap, } = inGuard;
645
- const condText = `${escapedOrig}.Is${memberN}()`;
646
- const thenCtx = {
647
- ...indent(ctxWithId),
648
- narrowedBindings: narrowedMap,
649
- };
650
- const thenInd = getIndent(thenCtx);
651
- const castLine = `${thenInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
652
- const [thenCode, thenBodyCtx] = emitForcedBlockWithPreamble(castLine, stmt.thenStatement, thenCtx, ind);
653
- let finalContext = dedent(thenBodyCtx);
654
- let code = `${ind}if (${condText})\n${thenCode}`;
655
- // Optional else branch narrowing (2-member unions only)
656
- if (stmt.elseStatement) {
657
- if (unionArity === 2) {
658
- const otherMemberN = memberN === 1 ? 2 : 1;
659
- const inlineExpr = `(${escapedOrig}.As${otherMemberN}())`;
660
- const elseNarrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
661
- elseNarrowedMap.set(originalName, {
662
- kind: "expr",
663
- exprText: inlineExpr,
664
- });
665
- const elseCtx = {
666
- ...indent({ ...finalContext, narrowedBindings: elseNarrowedMap }),
667
- };
668
- const [elseCode, elseCtxAfter] = emitStatement(stmt.elseStatement, elseCtx);
669
- code += `\n${ind}else\n${elseCode}`;
670
- finalContext = dedent(elseCtxAfter);
671
- finalContext = {
672
- ...finalContext,
673
- narrowedBindings: ctxWithId.narrowedBindings,
674
- };
675
- return [code, finalContext];
676
- }
677
- // If we can't narrow ELSE safely, emit it without narrowing.
678
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent({
679
- ...finalContext,
680
- narrowedBindings: ctxWithId.narrowedBindings,
681
- }));
682
- code += `\n${ind}else\n${elseCode}`;
683
- finalContext = dedent(elseCtx);
684
- finalContext = {
685
- ...finalContext,
686
- narrowedBindings: ctxWithId.narrowedBindings,
687
- };
688
- return [code, finalContext];
689
- }
690
- // Post-if narrowing for early-exit patterns (2-member unions only):
691
- // if (auth.Is2()) return ...;
692
- // // auth is now member 1 in the remainder
693
- if (unionArity === 2 && isDefinitelyTerminating(stmt.thenStatement)) {
694
- const otherMemberN = memberN === 1 ? 2 : 1;
695
- const inlineExpr = `(${escapedOrig}.As${otherMemberN}())`;
696
- const postMap = new Map(ctxWithId.narrowedBindings ?? []);
697
- postMap.set(originalName, { kind: "expr", exprText: inlineExpr });
698
- finalContext = { ...finalContext, narrowedBindings: postMap };
699
- return [code, finalContext];
700
- }
701
- // Restore narrowedBindings to the incoming scope.
702
- finalContext = {
703
- ...finalContext,
704
- narrowedBindings: ctxWithId.narrowedBindings,
705
- };
706
- return [code, finalContext];
707
- }
708
- // Case A4: if (shape.kind === "circle") { ... }
709
- // Discriminant literal equality narrowing → if (shape.IsN()) / if (!shape.IsN())
710
- const eqGuard = tryResolveDiscriminantEqualityGuard(stmt.condition, context);
711
- if (eqGuard) {
712
- const { originalName, operator, memberN, unionArity, ctxWithId, escapedOrig, escapedNarrow, narrowedMap, } = eqGuard;
713
- const isInequality = operator === "!==" || operator === "!=";
714
- const condText = isInequality
715
- ? `!${escapedOrig}.Is${memberN}()`
716
- : `${escapedOrig}.Is${memberN}()`;
717
- let code = `${ind}if (${condText})`;
718
- let finalContext = ctxWithId;
719
- // Equality: narrow THEN to memberN. Inequality: narrow ELSE to memberN.
720
- if (!isInequality) {
721
- const thenCtx = {
722
- ...indent(ctxWithId),
723
- narrowedBindings: narrowedMap,
724
- };
725
- const thenInd = getIndent(thenCtx);
726
- const castLine = `${thenInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
727
- const [thenCode, thenBodyCtx] = emitForcedBlockWithPreamble(castLine, stmt.thenStatement, thenCtx, ind);
728
- code += `\n${thenCode}`;
729
- finalContext = dedent(thenBodyCtx);
730
- if (stmt.elseStatement) {
731
- // Else-branch narrowing only for 2-member unions.
732
- if (unionArity === 2) {
733
- const otherMemberN = memberN === 1 ? 2 : 1;
734
- const inlineExpr = `(${escapedOrig}.As${otherMemberN}())`;
735
- const elseNarrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
736
- elseNarrowedMap.set(originalName, {
737
- kind: "expr",
738
- exprText: inlineExpr,
739
- });
740
- const elseCtx = {
741
- ...indent({ ...finalContext, narrowedBindings: elseNarrowedMap }),
742
- };
743
- const [elseCode, elseCtxAfter] = emitStatement(stmt.elseStatement, elseCtx);
744
- code += `\n${ind}else\n${elseCode}`;
745
- finalContext = dedent(elseCtxAfter);
746
- finalContext = {
747
- ...finalContext,
748
- narrowedBindings: ctxWithId.narrowedBindings,
749
- };
750
- return [code, finalContext];
751
- }
752
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent({
753
- ...finalContext,
754
- narrowedBindings: ctxWithId.narrowedBindings,
755
- }));
756
- code += `\n${ind}else\n${elseCode}`;
757
- finalContext = dedent(elseCtx);
758
- finalContext = {
759
- ...finalContext,
760
- narrowedBindings: ctxWithId.narrowedBindings,
761
- };
762
- return [code, finalContext];
763
- }
764
- // Post-if narrowing for early-exit patterns (2-member unions only).
765
- if (unionArity === 2 && isDefinitelyTerminating(stmt.thenStatement)) {
766
- const otherMemberN = memberN === 1 ? 2 : 1;
767
- const inlineExpr = `(${escapedOrig}.As${otherMemberN}())`;
768
- const postMap = new Map(ctxWithId.narrowedBindings ?? []);
769
- postMap.set(originalName, { kind: "expr", exprText: inlineExpr });
770
- finalContext = { ...finalContext, narrowedBindings: postMap };
771
- return [code, finalContext];
772
- }
773
- finalContext = {
774
- ...finalContext,
775
- narrowedBindings: ctxWithId.narrowedBindings,
776
- };
777
- return [code, finalContext];
778
- }
779
- // Inequality: THEN is "not memberN" (no narrowing unless arity==2), ELSE is memberN.
780
- {
781
- // Emit THEN (optionally narrowed to other member when arity==2).
782
- const [thenCode, thenCtxAfter] = (() => {
783
- if (unionArity === 2) {
784
- const otherMemberN = memberN === 1 ? 2 : 1;
785
- const inlineExpr = `(${escapedOrig}.As${otherMemberN}())`;
786
- const thenNarrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
787
- thenNarrowedMap.set(originalName, {
788
- kind: "expr",
789
- exprText: inlineExpr,
790
- });
791
- const thenCtx = {
792
- ...indent({ ...ctxWithId, narrowedBindings: thenNarrowedMap }),
793
- };
794
- return emitStatement(stmt.thenStatement, thenCtx);
795
- }
796
- return emitStatement(stmt.thenStatement, indent({ ...ctxWithId, narrowedBindings: ctxWithId.narrowedBindings }));
797
- })();
798
- code += `\n${thenCode}`;
799
- finalContext = dedent(thenCtxAfter);
800
- if (stmt.elseStatement) {
801
- const elseCtx = {
802
- ...indent(ctxWithId),
803
- narrowedBindings: narrowedMap,
804
- };
805
- const elseInd = getIndent(elseCtx);
806
- const castLine = `${elseInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
807
- const [elseCode, elseBodyCtx] = emitForcedBlockWithPreamble(castLine, stmt.elseStatement, elseCtx, ind);
808
- code += `\n${ind}else\n${elseCode}`;
809
- finalContext = dedent(elseBodyCtx);
810
- finalContext = {
811
- ...finalContext,
812
- narrowedBindings: ctxWithId.narrowedBindings,
813
- };
814
- return [code, finalContext];
815
- }
816
- // Post-if narrowing for early-exit patterns (2-member unions only):
817
- // if (!x.IsN()) return ...;
818
- // // x is now member N
819
- if (unionArity === 2 && isDefinitelyTerminating(stmt.thenStatement)) {
820
- const inlineExpr = `(${escapedOrig}.As${memberN}())`;
821
- const postMap = new Map(ctxWithId.narrowedBindings ?? []);
822
- postMap.set(originalName, { kind: "expr", exprText: inlineExpr });
823
- finalContext = { ...finalContext, narrowedBindings: postMap };
824
- return [code, finalContext];
825
- }
826
- finalContext = {
827
- ...finalContext,
828
- narrowedBindings: ctxWithId.narrowedBindings,
829
- };
830
- return [code, finalContext];
831
- }
832
- }
833
- // Case A2: if (x instanceof Foo) { ... }
834
- // C# pattern var narrowing → if (x is Foo x__is_k) { ... } (then-branch sees narrowed x)
835
- const instanceofGuard = tryResolveInstanceofGuard(stmt.condition, context);
836
- if (instanceofGuard) {
837
- const { ctxAfterRhs, escapedOrig, escapedNarrow, rhsTypeText, narrowedMap, } = instanceofGuard;
838
- const condText = `${escapedOrig} is ${rhsTypeText} ${escapedNarrow}`;
839
- const thenCtx = {
840
- ...indent(ctxAfterRhs),
841
- narrowedBindings: narrowedMap,
842
- };
843
- const [thenCode, thenCtxAfter] = emitStatement(stmt.thenStatement, thenCtx);
844
- let code = `${ind}if (${condText})\n${thenCode}`;
845
- let finalContext = dedent(thenCtxAfter);
846
- finalContext = {
847
- ...finalContext,
848
- narrowedBindings: ctxAfterRhs.narrowedBindings,
849
- };
850
- if (stmt.elseStatement) {
851
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent(finalContext));
852
- code += `\n${ind}else\n${elseCode}`;
853
- finalContext = dedent(elseCtx);
854
- }
855
- return [code, finalContext];
856
- }
857
- // Case B: if (!isUser(account)) { ... } else { ... }
858
- // Negated guard → for 2-member unions, narrow THEN to OTHER member, ELSE to guard's target
859
- if (stmt.condition.kind === "unary" &&
860
- stmt.condition.operator === "!" &&
861
- stmt.condition.expression.kind === "call" &&
862
- stmt.elseStatement) {
863
- const innerCall = stmt.condition.expression;
864
- const guard = tryResolvePredicateGuard(innerCall, context);
865
- if (guard) {
866
- const { originalName, memberN, unionArity, ctxWithId, escapedOrig, escapedNarrow, narrowedMap, } = guard;
867
- const condText = `!${escapedOrig}.Is${memberN}()`;
868
- // For 2-member unions: narrow THEN branch to the OTHER member
869
- // For N>2 unions: can't narrow THEN to a single type (it could be any of N-1 members)
870
- let thenCode;
871
- let thenCtx;
872
- if (unionArity === 2) {
873
- // Calculate the other member index (if memberN is 1, other is 2; if memberN is 2, other is 1)
874
- const otherMemberN = memberN === 1 ? 2 : 1;
875
- const nextId = (ctxWithId.tempVarId ?? 0) + 1;
876
- const thenCtxWithId = {
877
- ...ctxWithId,
878
- tempVarId: nextId,
879
- };
880
- const thenNarrowedName = `${originalName}__${otherMemberN}_${nextId}`;
881
- const escapedThenNarrow = escapeCSharpIdentifier(thenNarrowedName);
882
- const thenNarrowedMap = new Map(thenCtxWithId.narrowedBindings ?? []);
883
- thenNarrowedMap.set(originalName, {
884
- kind: "rename",
885
- name: thenNarrowedName,
886
- });
887
- const thenCtxNarrowed = {
888
- ...indent(thenCtxWithId),
889
- narrowedBindings: thenNarrowedMap,
890
- };
891
- const thenInd = getIndent(thenCtxNarrowed);
892
- const thenCastLine = `${thenInd}var ${escapedThenNarrow} = ${escapedOrig}.As${otherMemberN}();`;
893
- [thenCode, thenCtx] = emitForcedBlockWithPreamble(thenCastLine, stmt.thenStatement, thenCtxNarrowed, ind);
894
- }
895
- else {
896
- // N>2 unions: can't narrow THEN branch to a single type
897
- [thenCode, thenCtx] = emitStatement(stmt.thenStatement, indent(context));
898
- }
899
- // else branch: narrowing applies (to guard's target type)
900
- const elseCtxNarrowed = {
901
- ...indent(ctxWithId),
902
- narrowedBindings: narrowedMap,
903
- };
904
- const elseInd = getIndent(elseCtxNarrowed);
905
- const castLine = `${elseInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
906
- const [elseBlock, _elseBodyCtx] = emitForcedBlockWithPreamble(castLine, stmt.elseStatement, elseCtxNarrowed, ind);
907
- // Note: narrow bindings should not leak from if-else branches
908
- // We return thenCtx to preserve original semantics
909
- const code = `${ind}if (${condText})\n${thenCode}\n${ind}else\n${elseBlock}`;
910
- return [code, dedent(thenCtx)];
911
- }
912
- }
913
- // Case B2: if (!(x instanceof Foo)) { ... } else { ... }
914
- // Swap branches so ELSE runs under the narrowed pattern var.
915
- if (stmt.condition.kind === "unary" &&
916
- stmt.condition.operator === "!" &&
917
- stmt.elseStatement) {
918
- const inner = stmt.condition.expression;
919
- const guard = tryResolveInstanceofGuard(inner, context);
920
- if (guard) {
921
- const { ctxAfterRhs, escapedOrig, escapedNarrow, rhsTypeText, narrowedMap, } = guard;
922
- const condText = `${escapedOrig} is ${rhsTypeText} ${escapedNarrow}`;
923
- // THEN branch is the original ELSE (narrowed)
924
- const thenCtx = {
925
- ...indent(ctxAfterRhs),
926
- narrowedBindings: narrowedMap,
927
- };
928
- const [thenCode, thenCtxAfter] = emitStatement(stmt.elseStatement, thenCtx);
929
- // ELSE branch is the original THEN (not narrowed)
930
- const [elseCode, elseCtxAfter] = emitStatement(stmt.thenStatement, indent({
931
- ...dedent(thenCtxAfter),
932
- narrowedBindings: ctxAfterRhs.narrowedBindings,
933
- }));
934
- const code = `${ind}if (${condText})\n${thenCode}\n${ind}else\n${elseCode}`;
935
- return [code, dedent(elseCtxAfter)];
936
- }
937
- }
938
- // Case C: if (isUser(account) && account.foo) { ... }
939
- // Logical AND with predicate guard on left → nested-if lowering (preserves short-circuit)
940
- if (stmt.condition.kind === "logical" && stmt.condition.operator === "&&") {
941
- const left = stmt.condition.left;
942
- const right = stmt.condition.right;
943
- if (left.kind === "call") {
944
- const guard = tryResolvePredicateGuard(left, context);
945
- if (guard) {
946
- const { memberN, ctxWithId, escapedOrig, escapedNarrow, narrowedMap } = guard;
947
- // Outer: if (x.IsN())
948
- const outerCond = `${escapedOrig}.Is${memberN}()`;
949
- // Outer-then creates narrowed var
950
- const outerThenCtx = {
951
- ...indent(ctxWithId),
952
- narrowedBindings: narrowedMap,
953
- };
954
- const outerThenInd = getIndent(outerThenCtx);
955
- const castLine = `${outerThenInd}var ${escapedNarrow} = ${escapedOrig}.As${memberN}();`;
956
- // Emit RHS condition under narrowed context (TS semantics: rhs sees narrowed x)
957
- const [rhsFrag, rhsCtxAfterEmit] = emitExpression(right, outerThenCtx);
958
- const [rhsCondText, rhsCtxAfterCond] = toBooleanCondition(right, rhsFrag.text, rhsCtxAfterEmit);
959
- // When RHS true: emit original THEN under narrowed context
960
- const [thenCode, thenCtxAfter] = emitStatement(stmt.thenStatement, indent(rhsCtxAfterCond));
961
- // Helper to clear narrowing from context
962
- const clearNarrowing = (ctx) => ({
963
- ...ctx,
964
- narrowedBindings: ctxWithId.narrowedBindings,
965
- });
966
- let inner = `${outerThenInd}if (${rhsCondText})\n${thenCode}`;
967
- let currentCtx = dedent(thenCtxAfter);
968
- if (stmt.elseStatement) {
969
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent(clearNarrowing(currentCtx)));
970
- inner += `\n${outerThenInd}else\n${elseCode}`;
971
- currentCtx = dedent(elseCtx);
972
- }
973
- const outerThenBlock = `${ind}{\n${castLine}\n${inner}\n${ind}}`;
974
- // Outer else: emit ELSE as-is (no narrowing)
975
- let code = `${ind}if (${outerCond})\n${outerThenBlock}`;
976
- let finalContext = clearNarrowing(currentCtx);
977
- if (stmt.elseStatement) {
978
- const [outerElseCode, outerElseCtx] = emitStatement(stmt.elseStatement, indent(finalContext));
979
- code += `\n${ind}else\n${outerElseCode}`;
980
- finalContext = dedent(outerElseCtx);
981
- }
982
- return [code, finalContext];
983
- }
984
- }
985
- // Case C2: if (x instanceof Foo && x.foo) { ... }
986
- // Preserve short-circuit and expose narrowed x in RHS and THEN.
987
- if (left.kind === "binary" && left.operator === "instanceof") {
988
- const guard = tryResolveInstanceofGuard(left, context);
989
- if (guard) {
990
- const { ctxAfterRhs, escapedOrig, escapedNarrow, rhsTypeText, narrowedMap, } = guard;
991
- const rhsCtx = {
992
- ...ctxAfterRhs,
993
- narrowedBindings: narrowedMap,
994
- };
995
- const [rhsFrag, rhsCtxAfterEmit] = emitExpression(right, rhsCtx);
996
- const [rhsCondText, rhsCtxAfterCond] = toBooleanCondition(right, rhsFrag.text, rhsCtxAfterEmit);
997
- const condText = `(${escapedOrig} is ${rhsTypeText} ${escapedNarrow} && ${rhsCondText})`;
998
- const thenCtx = {
999
- ...indent(rhsCtxAfterCond),
1000
- narrowedBindings: narrowedMap,
1001
- };
1002
- const [thenCode, thenCtxAfter] = emitStatement(stmt.thenStatement, thenCtx);
1003
- let code = `${ind}if (${condText})\n${thenCode}`;
1004
- let finalContext = dedent(thenCtxAfter);
1005
- finalContext = {
1006
- ...finalContext,
1007
- narrowedBindings: ctxAfterRhs.narrowedBindings,
1008
- };
1009
- if (stmt.elseStatement) {
1010
- const [elseCode, elseCtx] = emitStatement(stmt.elseStatement, indent(finalContext));
1011
- code += `\n${ind}else\n${elseCode}`;
1012
- finalContext = dedent(elseCtx);
1013
- }
1014
- return [code, finalContext];
1015
- }
1016
- }
1017
- }
1018
- // Case D: Nullable value type narrowing
1019
- // if (id !== null) { ... } → id becomes id.Value in then-branch
1020
- const simpleNullableGuard = tryResolveSimpleNullableGuard(stmt.condition);
1021
- const nullableGuard = simpleNullableGuard ?? tryResolveNullableGuard(stmt.condition, context);
1022
- if (nullableGuard && nullableGuard.isValueType) {
1023
- const { key, targetExpr, narrowsInThen, strippedType } = nullableGuard;
1024
- // IMPORTANT: Avoid stacking `.Value` when:
1025
- // - we are emitting an else-if chain, and
1026
- // - an outer nullable guard already narrowed the identifier in the else-branch.
1027
- //
1028
- // Example (TS):
1029
- // if (x === undefined) { ... } else if (x !== undefined) { use(x) }
1030
- //
1031
- // In C#, we might narrow `x` in the outer ELSE (x.Value). If we build a new
1032
- // narrowed binding by reading `x` via emitIdentifier (which consults narrowedBindings),
1033
- // we'd accidentally create `x.Value.Value`.
1034
- //
1035
- // So: build the `.Value` access from the *raw* identifier (respecting CS0136 remaps),
1036
- // but ignoring existing narrowedBindings.
1037
- const [idFrag] = targetExpr.kind === "identifier"
1038
- ? emitIdentifier(targetExpr, {
1039
- ...context,
1040
- narrowedBindings: undefined,
1041
- })
1042
- : emitExpression(targetExpr, {
1043
- ...context,
1044
- narrowedBindings: undefined,
1045
- });
1046
- // Create narrowed binding: id → id.Value
1047
- const narrowedMap = new Map(context.narrowedBindings ?? []);
1048
- narrowedMap.set(key, {
1049
- kind: "expr",
1050
- exprText: `${idFrag.text}.Value`,
1051
- type: strippedType,
1052
- });
1053
- // Soundness: In compound conditions (A && B), we must NOT apply "else" narrowing.
1054
- // `!(A && (id == null))` does not imply `id != null` unless A is provably true.
1055
- //
1056
- // Therefore:
1057
- // - For simple guards: allow both THEN (id != null) and ELSE (id == null) narrowing.
1058
- // - For &&-nested guards: only allow THEN narrowing when the guard is `!= null`.
1059
- const isAndCondition = stmt.condition.kind === "logical" && stmt.condition.operator === "&&";
1060
- if (isAndCondition && !simpleNullableGuard && !narrowsInThen) {
1061
- // `id == null` inside `&&` - narrowing would only be valid in the THEN branch
1062
- // (and even then it's "id is null", not `.Value`-usable). Skip nullable rewrite.
1063
- // Fall through to standard if emission.
1064
- }
1065
- else {
1066
- // Emit condition (boolean context)
1067
- const [condText, condCtxAfterCond] = emitBooleanCondition(stmt.condition, (e, ctx) => emitExpression(e, ctx), context);
1068
- // Apply narrowing to appropriate branch
1069
- const thenCtx = {
1070
- ...indent(condCtxAfterCond),
1071
- narrowedBindings: narrowsInThen
1072
- ? narrowedMap
1073
- : condCtxAfterCond.narrowedBindings,
1074
- };
1075
- const [thenCode, thenCtxAfter] = emitStatement(stmt.thenStatement, thenCtx);
1076
- let code = `${ind}if (${condText})\n${thenCode}`;
1077
- let finalContext = dedent(thenCtxAfter);
1078
- // Clear narrowing after branch
1079
- finalContext = {
1080
- ...finalContext,
1081
- narrowedBindings: context.narrowedBindings,
1082
- };
1083
- if (stmt.elseStatement) {
1084
- const elseCtx = {
1085
- ...indent(finalContext),
1086
- narrowedBindings: !narrowsInThen
1087
- ? simpleNullableGuard
1088
- ? narrowedMap
1089
- : context.narrowedBindings
1090
- : context.narrowedBindings,
1091
- };
1092
- const [elseCode, elseCtxAfter] = emitStatement(stmt.elseStatement, elseCtx);
1093
- code += `\n${ind}else\n${elseCode}`;
1094
- finalContext = dedent(elseCtxAfter);
1095
- finalContext = {
1096
- ...finalContext,
1097
- narrowedBindings: context.narrowedBindings,
1098
- };
1099
- }
1100
- return [code, finalContext];
1101
- }
1102
- }
1103
- // Standard if-statement emission (no narrowing)
1104
- const [condText, condCtxAfterCond] = emitBooleanCondition(stmt.condition, (e, ctx) => emitExpression(e, ctx), context);
1105
- const [thenCode, thenContext] = emitStatement(stmt.thenStatement, indent(condCtxAfterCond));
1106
- let code = `${ind}if (${condText})\n${thenCode}`;
1107
- let finalContext = dedent(thenContext);
1108
- if (stmt.elseStatement) {
1109
- const [elseCode, elseContext] = emitStatement(stmt.elseStatement, indent(finalContext));
1110
- code += `\n${ind}else\n${elseCode}`;
1111
- finalContext = dedent(elseContext);
1112
- }
1113
- return [code, finalContext];
1114
- };
1115
- /**
1116
- * Emit a switch statement
1117
- */
1118
- export const emitSwitchStatement = (stmt, context) => {
1119
- const ind = getIndent(context);
1120
- const [exprFrag, exprContext] = emitExpression(stmt.expression, context);
1121
- let currentContext = indent(exprContext);
1122
- const caseInd = getIndent(currentContext);
1123
- const cases = [];
1124
- for (const switchCase of stmt.cases) {
1125
- if (switchCase.test) {
1126
- const [testFrag, testContext] = emitExpression(switchCase.test, currentContext);
1127
- currentContext = testContext;
1128
- cases.push(`${caseInd}case ${testFrag.text}:`);
1129
- }
1130
- else {
1131
- cases.push(`${caseInd}default:`);
1132
- }
1133
- const stmtContext = indent(currentContext);
1134
- for (const s of switchCase.statements) {
1135
- const [code, newContext] = emitStatement(s, stmtContext);
1136
- cases.push(code);
1137
- currentContext = newContext;
1138
- }
1139
- // Emit break only when case has non-empty body that doesn't terminate.
1140
- // Empty bodies represent intentional fall-through labels (TypeScript semantics).
1141
- const hasBody = switchCase.statements.length > 0;
1142
- if (hasBody) {
1143
- const lastStmt = switchCase.statements[switchCase.statements.length - 1];
1144
- const terminates = lastStmt?.kind === "breakStatement" ||
1145
- lastStmt?.kind === "returnStatement" ||
1146
- lastStmt?.kind === "throwStatement";
1147
- if (!terminates) {
1148
- cases.push(`${getIndent(stmtContext)}break;`);
1149
- }
1150
- }
1151
- }
1152
- const code = `${ind}switch (${exprFrag.text})\n${ind}{\n${cases.join("\n")}\n${ind}}`;
1153
- return [code, dedent(currentContext)];
1154
- };
4
+ export { emitIfStatementAst } from "./conditionals/if-emitter.js";
5
+ export { emitSwitchStatementAst } from "./conditionals/switch-emitter.js";
1155
6
  //# sourceMappingURL=conditionals.js.map