@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/adapter-generator.d.ts +5 -15
- package/dist/adapter-generator.d.ts.map +1 -1
- package/dist/adapter-generator.js +60 -53
- package/dist/adapter-generator.js.map +1 -1
- package/dist/contracts/backend-adapter.d.ts +36 -0
- package/dist/contracts/backend-adapter.d.ts.map +1 -0
- package/dist/contracts/backend-adapter.js +9 -0
- package/dist/contracts/backend-adapter.js.map +1 -0
- package/dist/contracts/emitter-contract.d.ts +54 -0
- package/dist/contracts/emitter-contract.d.ts.map +1 -0
- package/dist/contracts/emitter-contract.js +8 -0
- package/dist/contracts/emitter-contract.js.map +1 -0
- package/dist/contracts/import-classifier.d.ts +36 -0
- package/dist/contracts/import-classifier.d.ts.map +1 -0
- package/dist/contracts/import-classifier.js +9 -0
- package/dist/contracts/import-classifier.js.map +1 -0
- package/dist/core/{attributes.d.ts → format/attributes.d.ts} +9 -14
- package/dist/core/format/attributes.d.ts.map +1 -0
- package/dist/core/format/attributes.js +190 -0
- package/dist/core/format/attributes.js.map +1 -0
- package/dist/core/format/attributes.test.d.ts.map +1 -0
- package/dist/core/{attributes.test.js → format/attributes.test.js} +70 -32
- package/dist/core/format/attributes.test.js.map +1 -0
- package/dist/core/format/backend-ast/index.d.ts +7 -0
- package/dist/core/format/backend-ast/index.d.ts.map +1 -0
- package/dist/core/format/backend-ast/index.js +6 -0
- package/dist/core/format/backend-ast/index.js.map +1 -0
- package/dist/core/format/backend-ast/printer.d.ts +33 -0
- package/dist/core/format/backend-ast/printer.d.ts.map +1 -0
- package/dist/core/format/backend-ast/printer.js +1151 -0
- package/dist/core/format/backend-ast/printer.js.map +1 -0
- package/dist/core/format/backend-ast/types.d.ts +504 -0
- package/dist/core/format/backend-ast/types.d.ts.map +1 -0
- package/dist/core/format/backend-ast/types.js +13 -0
- package/dist/core/format/backend-ast/types.js.map +1 -0
- package/dist/core/format/backend-ast/utils.d.ts +26 -0
- package/dist/core/format/backend-ast/utils.d.ts.map +1 -0
- package/dist/core/format/backend-ast/utils.js +65 -0
- package/dist/core/format/backend-ast/utils.js.map +1 -0
- package/dist/core/{exports.d.ts → format/exports.d.ts} +2 -2
- package/dist/core/format/exports.d.ts.map +1 -0
- package/dist/core/format/exports.js +22 -0
- package/dist/core/format/exports.js.map +1 -0
- package/dist/core/format/index.d.ts +9 -0
- package/dist/core/format/index.d.ts.map +1 -0
- package/dist/core/format/index.js +9 -0
- package/dist/core/format/index.js.map +1 -0
- package/dist/core/{local-names.d.ts → format/local-names.d.ts} +1 -1
- package/dist/core/format/local-names.d.ts.map +1 -0
- package/dist/core/{local-names.js → format/local-names.js} +1 -1
- package/dist/core/format/local-names.js.map +1 -0
- package/dist/core/format/module-emitter/assembly.d.ts +27 -0
- package/dist/core/format/module-emitter/assembly.d.ts.map +1 -0
- package/dist/core/format/module-emitter/assembly.js +44 -0
- package/dist/core/format/module-emitter/assembly.js.map +1 -0
- package/dist/core/{module-emitter → format/module-emitter}/header.d.ts +1 -1
- package/dist/core/format/module-emitter/header.d.ts.map +1 -0
- package/dist/core/{module-emitter → format/module-emitter}/header.js +1 -1
- package/dist/core/format/module-emitter/header.js.map +1 -0
- package/dist/core/format/module-emitter/index.d.ts.map +1 -0
- package/dist/core/format/module-emitter/index.js.map +1 -0
- package/dist/core/format/module-emitter/namespace.d.ts +21 -0
- package/dist/core/format/module-emitter/namespace.d.ts.map +1 -0
- package/dist/core/format/module-emitter/namespace.js +58 -0
- package/dist/core/format/module-emitter/namespace.js.map +1 -0
- package/dist/core/{module-emitter → format/module-emitter}/orchestrator.d.ts +1 -1
- package/dist/core/format/module-emitter/orchestrator.d.ts.map +1 -0
- package/dist/core/{module-emitter → format/module-emitter}/orchestrator.js +19 -19
- package/dist/core/format/module-emitter/orchestrator.js.map +1 -0
- package/dist/core/format/module-emitter/separation.d.ts.map +1 -0
- package/dist/core/format/module-emitter/separation.js.map +1 -0
- package/dist/core/{module-emitter → format/module-emitter}/static-container.d.ts +8 -3
- package/dist/core/format/module-emitter/static-container.d.ts.map +1 -0
- package/dist/core/format/module-emitter/static-container.js +202 -0
- package/dist/core/format/module-emitter/static-container.js.map +1 -0
- package/dist/core/format/module-emitter.d.ts.map +1 -0
- package/dist/core/format/module-emitter.js.map +1 -0
- package/dist/core/{options.d.ts → format/options.d.ts} +1 -1
- package/dist/core/format/options.d.ts.map +1 -0
- package/dist/core/format/options.js.map +1 -0
- package/dist/core/index.d.ts +2 -7
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +2 -7
- package/dist/core/index.js.map +1 -1
- package/dist/core/semantic/boolean-context.d.ts +44 -0
- package/dist/core/semantic/boolean-context.d.ts.map +1 -0
- package/dist/core/semantic/boolean-context.js +717 -0
- package/dist/core/semantic/boolean-context.js.map +1 -0
- package/dist/core/semantic/boolean-context.test.d.ts.map +1 -0
- package/dist/core/{boolean-context.test.js → semantic/boolean-context.test.js} +86 -109
- package/dist/core/semantic/boolean-context.test.js.map +1 -0
- package/dist/core/{imports.d.ts → semantic/imports.d.ts} +1 -5
- package/dist/core/semantic/imports.d.ts.map +1 -0
- package/dist/core/{imports.js → semantic/imports.js} +56 -74
- package/dist/core/semantic/imports.js.map +1 -0
- package/dist/core/semantic/imports.test.d.ts.map +1 -0
- package/dist/core/semantic/imports.test.js +229 -0
- package/dist/core/semantic/imports.test.js.map +1 -0
- package/dist/core/semantic/index.d.ts +15 -0
- package/dist/core/semantic/index.d.ts.map +1 -0
- package/dist/core/semantic/index.js +15 -0
- package/dist/core/semantic/index.js.map +1 -0
- package/dist/core/{local-types.d.ts → semantic/local-types.d.ts} +1 -1
- package/dist/core/semantic/local-types.d.ts.map +1 -0
- package/dist/core/semantic/local-types.js.map +1 -0
- package/dist/core/{module-map.d.ts → semantic/module-map.d.ts} +1 -1
- package/dist/core/semantic/module-map.d.ts.map +1 -0
- package/dist/core/semantic/module-map.js.map +1 -0
- package/dist/core/semantic/module-map.test.d.ts.map +1 -0
- package/dist/core/semantic/module-map.test.js.map +1 -0
- package/dist/core/semantic/naming-collisions.d.ts.map +1 -0
- package/dist/core/{naming-collisions.js → semantic/naming-collisions.js} +1 -1
- package/dist/core/semantic/naming-collisions.js.map +1 -0
- package/dist/core/{type-alias-index.d.ts → semantic/type-alias-index.d.ts} +1 -1
- package/dist/core/semantic/type-alias-index.d.ts.map +1 -0
- package/dist/core/semantic/type-alias-index.js.map +1 -0
- package/dist/core/semantic/type-compatibility.d.ts.map +1 -0
- package/dist/core/semantic/type-compatibility.js.map +1 -0
- package/dist/core/{type-member-index.d.ts → semantic/type-member-index.d.ts} +1 -1
- package/dist/core/semantic/type-member-index.d.ts.map +1 -0
- package/dist/core/semantic/type-member-index.js.map +1 -0
- package/dist/core/semantic/type-params.d.ts.map +1 -0
- package/dist/core/semantic/type-params.js.map +1 -0
- package/dist/core/{type-resolution.d.ts → semantic/type-resolution.d.ts} +3 -6
- package/dist/core/semantic/type-resolution.d.ts.map +1 -0
- package/dist/core/{type-resolution.js → semantic/type-resolution.js} +15 -25
- package/dist/core/semantic/type-resolution.js.map +1 -0
- package/dist/core/semantic/type-resolution.test.d.ts.map +1 -0
- package/dist/core/{type-resolution.test.js → semantic/type-resolution.test.js} +11 -19
- package/dist/core/semantic/type-resolution.test.js.map +1 -0
- package/dist/core/semantic/unsafe.d.ts.map +1 -0
- package/dist/core/semantic/unsafe.js.map +1 -0
- package/dist/emitter-types/core.d.ts +6 -12
- package/dist/emitter-types/core.d.ts.map +1 -1
- package/dist/emitter-types/index.d.ts +1 -1
- package/dist/emitter-types/index.d.ts.map +1 -1
- package/dist/emitter-types/index.js.map +1 -1
- package/dist/emitter.d.ts +1 -1
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +9 -8
- package/dist/emitter.js.map +1 -1
- package/dist/expression-emitter.d.ts +10 -5
- package/dist/expression-emitter.d.ts.map +1 -1
- package/dist/expression-emitter.js +333 -117
- package/dist/expression-emitter.js.map +1 -1
- package/dist/expressions/access.d.ts +4 -3
- package/dist/expressions/access.d.ts.map +1 -1
- package/dist/expressions/access.js +252 -102
- package/dist/expressions/access.js.map +1 -1
- package/dist/expressions/calls/call-analysis.d.ts +86 -0
- package/dist/expressions/calls/call-analysis.d.ts.map +1 -0
- package/dist/expressions/calls/call-analysis.js +284 -0
- package/dist/expressions/calls/call-analysis.js.map +1 -0
- package/dist/expressions/calls/call-emitter.d.ts +13 -0
- package/dist/expressions/calls/call-emitter.d.ts.map +1 -0
- package/dist/expressions/calls/call-emitter.js +1048 -0
- package/dist/expressions/calls/call-emitter.js.map +1 -0
- package/dist/expressions/calls/new-emitter.d.ts +13 -0
- package/dist/expressions/calls/new-emitter.d.ts.map +1 -0
- package/dist/expressions/calls/new-emitter.js +641 -0
- package/dist/expressions/calls/new-emitter.js.map +1 -0
- package/dist/expressions/calls.d.ts +2 -14
- package/dist/expressions/calls.d.ts.map +1 -1
- package/dist/expressions/calls.js +2 -909
- package/dist/expressions/calls.js.map +1 -1
- package/dist/expressions/collections.d.ts +6 -16
- package/dist/expressions/collections.d.ts.map +1 -1
- package/dist/expressions/collections.js +318 -217
- package/dist/expressions/collections.js.map +1 -1
- package/dist/expressions/functions.d.ts +6 -5
- package/dist/expressions/functions.d.ts.map +1 -1
- package/dist/expressions/functions.js +57 -62
- package/dist/expressions/functions.js.map +1 -1
- package/dist/expressions/identifiers.d.ts +11 -6
- package/dist/expressions/identifiers.d.ts.map +1 -1
- package/dist/expressions/identifiers.js +76 -25
- package/dist/expressions/identifiers.js.map +1 -1
- package/dist/expressions/index.d.ts +1 -1
- package/dist/expressions/index.d.ts.map +1 -1
- package/dist/expressions/index.js +1 -1
- package/dist/expressions/index.js.map +1 -1
- package/dist/expressions/index.test.js +605 -0
- package/dist/expressions/index.test.js.map +1 -1
- package/dist/expressions/literals.d.ts +4 -3
- package/dist/expressions/literals.d.ts.map +1 -1
- package/dist/expressions/literals.js +25 -17
- package/dist/expressions/literals.js.map +1 -1
- package/dist/expressions/literals.test.js +18 -18
- package/dist/expressions/literals.test.js.map +1 -1
- package/dist/expressions/operators/assignment-emitter.d.ts +16 -0
- package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -0
- package/dist/expressions/operators/assignment-emitter.js +118 -0
- package/dist/expressions/operators/assignment-emitter.js.map +1 -0
- package/dist/expressions/operators/binary-emitter.d.ts +33 -0
- package/dist/expressions/operators/binary-emitter.d.ts.map +1 -0
- package/dist/expressions/operators/binary-emitter.js +398 -0
- package/dist/expressions/operators/binary-emitter.js.map +1 -0
- package/dist/expressions/operators/conditional-emitter.d.ts +17 -0
- package/dist/expressions/operators/conditional-emitter.d.ts.map +1 -0
- package/dist/expressions/operators/conditional-emitter.js +306 -0
- package/dist/expressions/operators/conditional-emitter.js.map +1 -0
- package/dist/expressions/operators/helpers.d.ts +37 -0
- package/dist/expressions/operators/helpers.d.ts.map +1 -0
- package/dist/expressions/operators/helpers.js +136 -0
- package/dist/expressions/operators/helpers.js.map +1 -0
- package/dist/expressions/operators/logical-emitter.d.ts +23 -0
- package/dist/expressions/operators/logical-emitter.d.ts.map +1 -0
- package/dist/expressions/operators/logical-emitter.js +73 -0
- package/dist/expressions/operators/logical-emitter.js.map +1 -0
- package/dist/expressions/operators/unary-emitter.d.ts +30 -0
- package/dist/expressions/operators/unary-emitter.d.ts.map +1 -0
- package/dist/expressions/operators/unary-emitter.js +244 -0
- package/dist/expressions/operators/unary-emitter.js.map +1 -0
- package/dist/expressions/operators.d.ts +5 -81
- package/dist/expressions/operators.d.ts.map +1 -1
- package/dist/expressions/operators.js +5 -949
- package/dist/expressions/operators.js.map +1 -1
- package/dist/expressions/other.d.ts +15 -11
- package/dist/expressions/other.d.ts.map +1 -1
- package/dist/expressions/other.js +51 -36
- package/dist/expressions/other.js.map +1 -1
- package/dist/expressions/precedence.test.js +1 -1
- package/dist/expressions/precedence.test.js.map +1 -1
- package/dist/generator-exchange.d.ts +10 -3
- package/dist/generator-exchange.d.ts.map +1 -1
- package/dist/generator-exchange.js +57 -54
- package/dist/generator-exchange.js.map +1 -1
- package/dist/generator-wrapper.d.ts +17 -65
- package/dist/generator-wrapper.d.ts.map +1 -1
- package/dist/generator-wrapper.js +396 -220
- package/dist/generator-wrapper.js.map +1 -1
- package/dist/generator-wrapper.test.js +22 -14
- package/dist/generator-wrapper.test.js.map +1 -1
- package/dist/integration.test.js +169 -1
- package/dist/integration.test.js.map +1 -1
- package/dist/patterns.d.ts +18 -88
- package/dist/patterns.d.ts.map +1 -1
- package/dist/patterns.js +540 -304
- package/dist/patterns.js.map +1 -1
- package/dist/patterns.test.js +5 -4
- package/dist/patterns.test.js.map +1 -1
- package/dist/specialization/generation.d.ts +7 -3
- package/dist/specialization/generation.d.ts.map +1 -1
- package/dist/specialization/generation.js +31 -15
- package/dist/specialization/generation.js.map +1 -1
- package/dist/specialization/type-aliases.test.js +47 -2
- package/dist/specialization/type-aliases.test.js.map +1 -1
- package/dist/statement-emitter.d.ts +15 -4
- package/dist/statement-emitter.d.ts.map +1 -1
- package/dist/statement-emitter.js +54 -47
- package/dist/statement-emitter.js.map +1 -1
- package/dist/statements/blocks.d.ts +24 -16
- package/dist/statements/blocks.d.ts.map +1 -1
- package/dist/statements/blocks.js +242 -65
- package/dist/statements/blocks.js.map +1 -1
- package/dist/statements/classes/index.d.ts +1 -1
- package/dist/statements/classes/index.d.ts.map +1 -1
- package/dist/statements/classes/index.js +1 -1
- package/dist/statements/classes/index.js.map +1 -1
- package/dist/statements/classes/inline-types.d.ts +4 -3
- package/dist/statements/classes/inline-types.d.ts.map +1 -1
- package/dist/statements/classes/inline-types.js +21 -21
- package/dist/statements/classes/inline-types.js.map +1 -1
- package/dist/statements/classes/members/constructors.d.ts +4 -3
- package/dist/statements/classes/members/constructors.d.ts.map +1 -1
- package/dist/statements/classes/members/constructors.js +57 -58
- package/dist/statements/classes/members/constructors.js.map +1 -1
- package/dist/statements/classes/members/methods.d.ts +4 -3
- package/dist/statements/classes/members/methods.d.ts.map +1 -1
- package/dist/statements/classes/members/methods.js +106 -101
- package/dist/statements/classes/members/methods.js.map +1 -1
- package/dist/statements/classes/members/orchestrator.d.ts +4 -3
- package/dist/statements/classes/members/orchestrator.d.ts.map +1 -1
- package/dist/statements/classes/members/orchestrator.js +2 -2
- package/dist/statements/classes/members/orchestrator.js.map +1 -1
- package/dist/statements/classes/members/properties.d.ts +4 -3
- package/dist/statements/classes/members/properties.d.ts.map +1 -1
- package/dist/statements/classes/members/properties.js +105 -88
- package/dist/statements/classes/members/properties.js.map +1 -1
- package/dist/statements/classes/members/shadowing.test.js +7 -2
- package/dist/statements/classes/members/shadowing.test.js.map +1 -1
- package/dist/statements/classes/members/static-readonly-properties.test.js +4 -1
- package/dist/statements/classes/members/static-readonly-properties.test.js.map +1 -1
- package/dist/statements/classes/parameters.d.ts +8 -10
- package/dist/statements/classes/parameters.d.ts.map +1 -1
- package/dist/statements/classes/parameters.js +30 -26
- package/dist/statements/classes/parameters.js.map +1 -1
- package/dist/statements/classes/properties.d.ts +4 -3
- package/dist/statements/classes/properties.d.ts.map +1 -1
- package/dist/statements/classes/properties.js +76 -50
- package/dist/statements/classes/properties.js.map +1 -1
- package/dist/statements/classes.d.ts +1 -1
- package/dist/statements/classes.d.ts.map +1 -1
- package/dist/statements/classes.js +1 -1
- package/dist/statements/classes.js.map +1 -1
- package/dist/statements/control/conditionals/guard-analysis.d.ts +169 -0
- package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -0
- package/dist/statements/control/conditionals/guard-analysis.js +591 -0
- package/dist/statements/control/conditionals/guard-analysis.js.map +1 -0
- package/dist/statements/control/conditionals/if-emitter.d.ts +14 -0
- package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -0
- package/dist/statements/control/conditionals/if-emitter.js +725 -0
- package/dist/statements/control/conditionals/if-emitter.js.map +1 -0
- package/dist/statements/control/conditionals/switch-emitter.d.ts +13 -0
- package/dist/statements/control/conditionals/switch-emitter.d.ts.map +1 -0
- package/dist/statements/control/conditionals/switch-emitter.js +60 -0
- package/dist/statements/control/conditionals/switch-emitter.js.map +1 -0
- package/dist/statements/control/conditionals.d.ts +3 -15
- package/dist/statements/control/conditionals.d.ts.map +1 -1
- package/dist/statements/control/conditionals.js +3 -1152
- package/dist/statements/control/conditionals.js.map +1 -1
- package/dist/statements/control/exceptions.d.ts +8 -6
- package/dist/statements/control/exceptions.d.ts.map +1 -1
- package/dist/statements/control/exceptions.js +35 -23
- package/dist/statements/control/exceptions.js.map +1 -1
- package/dist/statements/control/index.d.ts +3 -3
- package/dist/statements/control/index.d.ts.map +1 -1
- package/dist/statements/control/index.js +3 -3
- package/dist/statements/control/index.js.map +1 -1
- package/dist/statements/control/loops.d.ts +14 -12
- package/dist/statements/control/loops.d.ts.map +1 -1
- package/dist/statements/control/loops.js +147 -82
- package/dist/statements/control/loops.js.map +1 -1
- package/dist/statements/control.d.ts +1 -1
- package/dist/statements/control.d.ts.map +1 -1
- package/dist/statements/control.js +1 -1
- package/dist/statements/control.js.map +1 -1
- package/dist/statements/declarations/classes.d.ts +7 -3
- package/dist/statements/declarations/classes.d.ts.map +1 -1
- package/dist/statements/declarations/classes.js +107 -83
- package/dist/statements/declarations/classes.js.map +1 -1
- package/dist/statements/declarations/enums.d.ts +4 -3
- package/dist/statements/declarations/enums.d.ts.map +1 -1
- package/dist/statements/declarations/enums.js +16 -15
- package/dist/statements/declarations/enums.js.map +1 -1
- package/dist/statements/declarations/functions.d.ts +18 -2
- package/dist/statements/declarations/functions.d.ts.map +1 -1
- package/dist/statements/declarations/functions.js +633 -162
- package/dist/statements/declarations/functions.js.map +1 -1
- package/dist/statements/declarations/index.d.ts +2 -2
- package/dist/statements/declarations/index.d.ts.map +1 -1
- package/dist/statements/declarations/index.js +2 -2
- package/dist/statements/declarations/index.js.map +1 -1
- package/dist/statements/declarations/interfaces.d.ts +7 -3
- package/dist/statements/declarations/interfaces.d.ts.map +1 -1
- package/dist/statements/declarations/interfaces.js +138 -105
- package/dist/statements/declarations/interfaces.js.map +1 -1
- package/dist/statements/declarations/type-aliases.d.ts +7 -3
- package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
- package/dist/statements/declarations/type-aliases.js +122 -82
- package/dist/statements/declarations/type-aliases.js.map +1 -1
- package/dist/statements/declarations/variables.d.ts +12 -2
- package/dist/statements/declarations/variables.d.ts.map +1 -1
- package/dist/statements/declarations/variables.js +541 -428
- package/dist/statements/declarations/variables.js.map +1 -1
- package/dist/statements/declarations.d.ts +1 -1
- package/dist/statements/declarations.d.ts.map +1 -1
- package/dist/statements/declarations.js +1 -1
- package/dist/statements/declarations.js.map +1 -1
- package/dist/statements/index.d.ts +3 -3
- package/dist/statements/index.d.ts.map +1 -1
- package/dist/statements/index.js +5 -5
- package/dist/statements/index.js.map +1 -1
- package/dist/statements/index.test.js +307 -0
- package/dist/statements/index.test.js.map +1 -1
- package/dist/type-emitter.d.ts +1 -1
- package/dist/type-emitter.d.ts.map +1 -1
- package/dist/type-emitter.js +1 -1
- package/dist/type-emitter.js.map +1 -1
- package/dist/types/arrays.d.ts +3 -2
- package/dist/types/arrays.d.ts.map +1 -1
- package/dist/types/arrays.js +7 -5
- package/dist/types/arrays.js.map +1 -1
- package/dist/types/dictionaries.d.ts +6 -3
- package/dist/types/dictionaries.d.ts.map +1 -1
- package/dist/types/dictionaries.js +22 -10
- package/dist/types/dictionaries.js.map +1 -1
- package/dist/types/emitter.d.ts +8 -2
- package/dist/types/emitter.d.ts.map +1 -1
- package/dist/types/emitter.js +20 -6
- package/dist/types/emitter.js.map +1 -1
- package/dist/types/functions.d.ts +3 -2
- package/dist/types/functions.d.ts.map +1 -1
- package/dist/types/functions.js +36 -13
- package/dist/types/functions.js.map +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -2
- package/dist/types/index.js.map +1 -1
- package/dist/types/index.test.js +137 -0
- package/dist/types/index.test.js.map +1 -1
- package/dist/types/intersections.d.ts +3 -2
- package/dist/types/intersections.d.ts.map +1 -1
- package/dist/types/intersections.js +2 -2
- package/dist/types/intersections.js.map +1 -1
- package/dist/types/literals.d.ts +9 -2
- package/dist/types/literals.d.ts.map +1 -1
- package/dist/types/literals.js +11 -5
- package/dist/types/literals.js.map +1 -1
- package/dist/types/objects.d.ts +3 -2
- package/dist/types/objects.d.ts.map +1 -1
- package/dist/types/objects.js +1 -1
- package/dist/types/objects.js.map +1 -1
- package/dist/types/parameters.d.ts +6 -5
- package/dist/types/parameters.d.ts.map +1 -1
- package/dist/types/parameters.js +88 -29
- package/dist/types/parameters.js.map +1 -1
- package/dist/types/parameters.test.js +9 -4
- package/dist/types/parameters.test.js.map +1 -1
- package/dist/types/primitives.d.ts +5 -4
- package/dist/types/primitives.d.ts.map +1 -1
- package/dist/types/primitives.js +14 -13
- package/dist/types/primitives.js.map +1 -1
- package/dist/types/references.d.ts +3 -2
- package/dist/types/references.d.ts.map +1 -1
- package/dist/types/references.js +135 -111
- package/dist/types/references.js.map +1 -1
- package/dist/types/tuples.d.ts +3 -2
- package/dist/types/tuples.d.ts.map +1 -1
- package/dist/types/tuples.js +25 -11
- package/dist/types/tuples.js.map +1 -1
- package/dist/types/unions.d.ts +3 -2
- package/dist/types/unions.d.ts.map +1 -1
- package/dist/types/unions.js +36 -21
- package/dist/types/unions.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +2 -2
- package/dist/core/attributes.d.ts.map +0 -1
- package/dist/core/attributes.js +0 -141
- package/dist/core/attributes.js.map +0 -1
- package/dist/core/attributes.test.d.ts.map +0 -1
- package/dist/core/attributes.test.js.map +0 -1
- package/dist/core/boolean-context.d.ts +0 -42
- package/dist/core/boolean-context.d.ts.map +0 -1
- package/dist/core/boolean-context.js +0 -442
- package/dist/core/boolean-context.js.map +0 -1
- package/dist/core/boolean-context.test.d.ts.map +0 -1
- package/dist/core/boolean-context.test.js.map +0 -1
- package/dist/core/exports.d.ts.map +0 -1
- package/dist/core/exports.js +0 -28
- package/dist/core/exports.js.map +0 -1
- package/dist/core/imports.d.ts.map +0 -1
- package/dist/core/imports.js.map +0 -1
- package/dist/core/imports.test.d.ts.map +0 -1
- package/dist/core/imports.test.js +0 -79
- package/dist/core/imports.test.js.map +0 -1
- package/dist/core/local-names.d.ts.map +0 -1
- package/dist/core/local-names.js.map +0 -1
- package/dist/core/local-types.d.ts.map +0 -1
- package/dist/core/local-types.js.map +0 -1
- package/dist/core/module-emitter/assembly.d.ts +0 -24
- package/dist/core/module-emitter/assembly.d.ts.map +0 -1
- package/dist/core/module-emitter/assembly.js +0 -69
- package/dist/core/module-emitter/assembly.js.map +0 -1
- package/dist/core/module-emitter/header.d.ts.map +0 -1
- package/dist/core/module-emitter/header.js.map +0 -1
- package/dist/core/module-emitter/index.d.ts.map +0 -1
- package/dist/core/module-emitter/index.js.map +0 -1
- package/dist/core/module-emitter/namespace.d.ts +0 -14
- package/dist/core/module-emitter/namespace.d.ts.map +0 -1
- package/dist/core/module-emitter/namespace.js +0 -26
- package/dist/core/module-emitter/namespace.js.map +0 -1
- package/dist/core/module-emitter/orchestrator.d.ts.map +0 -1
- package/dist/core/module-emitter/orchestrator.js.map +0 -1
- package/dist/core/module-emitter/separation.d.ts.map +0 -1
- package/dist/core/module-emitter/separation.js.map +0 -1
- package/dist/core/module-emitter/static-container.d.ts.map +0 -1
- package/dist/core/module-emitter/static-container.js +0 -139
- package/dist/core/module-emitter/static-container.js.map +0 -1
- package/dist/core/module-emitter.d.ts.map +0 -1
- package/dist/core/module-emitter.js.map +0 -1
- package/dist/core/module-map.d.ts.map +0 -1
- package/dist/core/module-map.js.map +0 -1
- package/dist/core/module-map.test.d.ts.map +0 -1
- package/dist/core/module-map.test.js.map +0 -1
- package/dist/core/naming-collisions.d.ts.map +0 -1
- package/dist/core/naming-collisions.js.map +0 -1
- package/dist/core/options.d.ts.map +0 -1
- package/dist/core/options.js.map +0 -1
- package/dist/core/type-alias-index.d.ts.map +0 -1
- package/dist/core/type-alias-index.js.map +0 -1
- package/dist/core/type-compatibility.d.ts.map +0 -1
- package/dist/core/type-compatibility.js.map +0 -1
- package/dist/core/type-member-index.d.ts.map +0 -1
- package/dist/core/type-member-index.js.map +0 -1
- package/dist/core/type-params.d.ts.map +0 -1
- package/dist/core/type-params.js.map +0 -1
- package/dist/core/type-resolution.d.ts.map +0 -1
- package/dist/core/type-resolution.js.map +0 -1
- package/dist/core/type-resolution.test.d.ts.map +0 -1
- package/dist/core/type-resolution.test.js.map +0 -1
- package/dist/core/unsafe.d.ts.map +0 -1
- package/dist/core/unsafe.js.map +0 -1
- /package/dist/core/{attributes.test.d.ts → format/attributes.test.d.ts} +0 -0
- /package/dist/core/{module-emitter → format/module-emitter}/index.d.ts +0 -0
- /package/dist/core/{module-emitter → format/module-emitter}/index.js +0 -0
- /package/dist/core/{module-emitter → format/module-emitter}/separation.d.ts +0 -0
- /package/dist/core/{module-emitter → format/module-emitter}/separation.js +0 -0
- /package/dist/core/{module-emitter.d.ts → format/module-emitter.d.ts} +0 -0
- /package/dist/core/{module-emitter.js → format/module-emitter.js} +0 -0
- /package/dist/core/{options.js → format/options.js} +0 -0
- /package/dist/core/{boolean-context.test.d.ts → semantic/boolean-context.test.d.ts} +0 -0
- /package/dist/core/{imports.test.d.ts → semantic/imports.test.d.ts} +0 -0
- /package/dist/core/{local-types.js → semantic/local-types.js} +0 -0
- /package/dist/core/{module-map.js → semantic/module-map.js} +0 -0
- /package/dist/core/{module-map.test.d.ts → semantic/module-map.test.d.ts} +0 -0
- /package/dist/core/{module-map.test.js → semantic/module-map.test.js} +0 -0
- /package/dist/core/{naming-collisions.d.ts → semantic/naming-collisions.d.ts} +0 -0
- /package/dist/core/{type-alias-index.js → semantic/type-alias-index.js} +0 -0
- /package/dist/core/{type-compatibility.d.ts → semantic/type-compatibility.d.ts} +0 -0
- /package/dist/core/{type-compatibility.js → semantic/type-compatibility.js} +0 -0
- /package/dist/core/{type-member-index.js → semantic/type-member-index.js} +0 -0
- /package/dist/core/{type-params.d.ts → semantic/type-params.d.ts} +0 -0
- /package/dist/core/{type-params.js → semantic/type-params.js} +0 -0
- /package/dist/core/{type-resolution.test.d.ts → semantic/type-resolution.test.d.ts} +0 -0
- /package/dist/core/{unsafe.d.ts → semantic/unsafe.d.ts} +0 -0
- /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
|
-
|
|
5
|
-
|
|
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
|