@tsonic/emitter 0.0.72 → 0.0.74

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 (207) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/adapter-generator.d.ts.map +1 -1
  3. package/dist/adapter-generator.js +2 -1
  4. package/dist/adapter-generator.js.map +1 -1
  5. package/dist/constants.d.ts +3 -2
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +15 -6
  8. package/dist/constants.js.map +1 -1
  9. package/dist/core/format/attributes.d.ts.map +1 -1
  10. package/dist/core/format/attributes.js +5 -56
  11. package/dist/core/format/attributes.js.map +1 -1
  12. package/dist/core/format/attributes.test.js +1 -1
  13. package/dist/core/format/attributes.test.js.map +1 -1
  14. package/dist/core/format/backend-ast/builders.d.ts +13 -0
  15. package/dist/core/format/backend-ast/builders.d.ts.map +1 -0
  16. package/dist/core/format/backend-ast/builders.js +169 -0
  17. package/dist/core/format/backend-ast/builders.js.map +1 -0
  18. package/dist/core/format/backend-ast/builders.test.d.ts +2 -0
  19. package/dist/core/format/backend-ast/builders.test.d.ts.map +1 -0
  20. package/dist/core/format/backend-ast/builders.test.js +258 -0
  21. package/dist/core/format/backend-ast/builders.test.js.map +1 -0
  22. package/dist/core/format/backend-ast/index.d.ts +3 -2
  23. package/dist/core/format/backend-ast/index.d.ts.map +1 -1
  24. package/dist/core/format/backend-ast/index.js +2 -1
  25. package/dist/core/format/backend-ast/index.js.map +1 -1
  26. package/dist/core/format/backend-ast/invariants.test.d.ts +2 -0
  27. package/dist/core/format/backend-ast/invariants.test.d.ts.map +1 -0
  28. package/dist/core/format/backend-ast/invariants.test.js +72 -0
  29. package/dist/core/format/backend-ast/invariants.test.js.map +1 -0
  30. package/dist/core/format/backend-ast/printer.d.ts +1 -1
  31. package/dist/core/format/backend-ast/printer.d.ts.map +1 -1
  32. package/dist/core/format/backend-ast/printer.js +362 -119
  33. package/dist/core/format/backend-ast/printer.js.map +1 -1
  34. package/dist/core/format/backend-ast/printer.test.d.ts +2 -0
  35. package/dist/core/format/backend-ast/printer.test.d.ts.map +1 -0
  36. package/dist/core/format/backend-ast/printer.test.js +796 -0
  37. package/dist/core/format/backend-ast/printer.test.js.map +1 -0
  38. package/dist/core/format/backend-ast/types.d.ts +66 -14
  39. package/dist/core/format/backend-ast/types.d.ts.map +1 -1
  40. package/dist/core/format/backend-ast/utils.d.ts +12 -8
  41. package/dist/core/format/backend-ast/utils.d.ts.map +1 -1
  42. package/dist/core/format/backend-ast/utils.js +222 -19
  43. package/dist/core/format/backend-ast/utils.js.map +1 -1
  44. package/dist/core/format/backend-ast/utils.test.d.ts +2 -0
  45. package/dist/core/format/backend-ast/utils.test.d.ts.map +1 -0
  46. package/dist/core/format/backend-ast/utils.test.js +142 -0
  47. package/dist/core/format/backend-ast/utils.test.js.map +1 -0
  48. package/dist/core/format/module-emitter/assembly.d.ts +2 -2
  49. package/dist/core/format/module-emitter/assembly.d.ts.map +1 -1
  50. package/dist/core/format/module-emitter/assembly.js +7 -3
  51. package/dist/core/format/module-emitter/assembly.js.map +1 -1
  52. package/dist/core/format/module-emitter/header.d.ts +2 -1
  53. package/dist/core/format/module-emitter/header.d.ts.map +1 -1
  54. package/dist/core/format/module-emitter/header.js +2 -2
  55. package/dist/core/format/module-emitter/header.js.map +1 -1
  56. package/dist/core/format/module-emitter/orchestrator.js +1 -1
  57. package/dist/core/format/module-emitter/orchestrator.js.map +1 -1
  58. package/dist/core/format/module-emitter/static-container.d.ts.map +1 -1
  59. package/dist/core/format/module-emitter/static-container.js +3 -5
  60. package/dist/core/format/module-emitter/static-container.js.map +1 -1
  61. package/dist/core/module-emitter.test.js +1 -0
  62. package/dist/core/module-emitter.test.js.map +1 -1
  63. package/dist/core/semantic/boolean-context.d.ts.map +1 -1
  64. package/dist/core/semantic/boolean-context.js +45 -49
  65. package/dist/core/semantic/boolean-context.js.map +1 -1
  66. package/dist/core/semantic/imports.d.ts.map +1 -1
  67. package/dist/core/semantic/imports.js +25 -15
  68. package/dist/core/semantic/imports.js.map +1 -1
  69. package/dist/core/semantic/imports.test.js +43 -0
  70. package/dist/core/semantic/imports.test.js.map +1 -1
  71. package/dist/core/semantic/type-resolution.d.ts +1 -0
  72. package/dist/core/semantic/type-resolution.d.ts.map +1 -1
  73. package/dist/core/semantic/type-resolution.js +166 -5
  74. package/dist/core/semantic/type-resolution.js.map +1 -1
  75. package/dist/core/semantic/type-resolution.test.js +35 -0
  76. package/dist/core/semantic/type-resolution.test.js.map +1 -1
  77. package/dist/emitter-types/core.d.ts +24 -10
  78. package/dist/emitter-types/core.d.ts.map +1 -1
  79. package/dist/emitter.d.ts.map +1 -1
  80. package/dist/emitter.js +124 -44
  81. package/dist/emitter.js.map +1 -1
  82. package/dist/expression-emitter.d.ts.map +1 -1
  83. package/dist/expression-emitter.js +797 -17
  84. package/dist/expression-emitter.js.map +1 -1
  85. package/dist/expressions/access.d.ts.map +1 -1
  86. package/dist/expressions/access.js +65 -25
  87. package/dist/expressions/access.js.map +1 -1
  88. package/dist/expressions/calls/call-analysis.d.ts +0 -10
  89. package/dist/expressions/calls/call-analysis.d.ts.map +1 -1
  90. package/dist/expressions/calls/call-analysis.js +3 -62
  91. package/dist/expressions/calls/call-analysis.js.map +1 -1
  92. package/dist/expressions/calls/call-emitter.d.ts.map +1 -1
  93. package/dist/expressions/calls/call-emitter.js +97 -196
  94. package/dist/expressions/calls/call-emitter.js.map +1 -1
  95. package/dist/expressions/calls/new-emitter.d.ts.map +1 -1
  96. package/dist/expressions/calls/new-emitter.js +36 -69
  97. package/dist/expressions/calls/new-emitter.js.map +1 -1
  98. package/dist/expressions/collections.d.ts +3 -0
  99. package/dist/expressions/collections.d.ts.map +1 -1
  100. package/dist/expressions/collections.js +238 -57
  101. package/dist/expressions/collections.js.map +1 -1
  102. package/dist/expressions/functions.d.ts.map +1 -1
  103. package/dist/expressions/functions.js +1 -7
  104. package/dist/expressions/functions.js.map +1 -1
  105. package/dist/expressions/identifiers.d.ts.map +1 -1
  106. package/dist/expressions/identifiers.js +24 -38
  107. package/dist/expressions/identifiers.js.map +1 -1
  108. package/dist/expressions/index.test.js +245 -0
  109. package/dist/expressions/index.test.js.map +1 -1
  110. package/dist/expressions/literals.d.ts.map +1 -1
  111. package/dist/expressions/literals.js +9 -41
  112. package/dist/expressions/literals.js.map +1 -1
  113. package/dist/expressions/operators/assignment-emitter.d.ts.map +1 -1
  114. package/dist/expressions/operators/assignment-emitter.js +2 -6
  115. package/dist/expressions/operators/assignment-emitter.js.map +1 -1
  116. package/dist/expressions/operators/binary-emitter.d.ts.map +1 -1
  117. package/dist/expressions/operators/binary-emitter.js +102 -77
  118. package/dist/expressions/operators/binary-emitter.js.map +1 -1
  119. package/dist/expressions/operators/logical-emitter.d.ts.map +1 -1
  120. package/dist/expressions/operators/logical-emitter.js +1 -3
  121. package/dist/expressions/operators/logical-emitter.js.map +1 -1
  122. package/dist/expressions/operators/unary-emitter.d.ts.map +1 -1
  123. package/dist/expressions/operators/unary-emitter.js +9 -20
  124. package/dist/expressions/operators/unary-emitter.js.map +1 -1
  125. package/dist/expressions/other.d.ts.map +1 -1
  126. package/dist/expressions/other.js +57 -4
  127. package/dist/expressions/other.js.map +1 -1
  128. package/dist/generator-exchange.d.ts.map +1 -1
  129. package/dist/generator-exchange.js +3 -2
  130. package/dist/generator-exchange.js.map +1 -1
  131. package/dist/generator-wrapper.d.ts.map +1 -1
  132. package/dist/generator-wrapper.js +27 -56
  133. package/dist/generator-wrapper.js.map +1 -1
  134. package/dist/integration.test.js +393 -5
  135. package/dist/integration.test.js.map +1 -1
  136. package/dist/json-aot-generic.test.js +3 -0
  137. package/dist/json-aot-generic.test.js.map +1 -1
  138. package/dist/patterns.d.ts.map +1 -1
  139. package/dist/patterns.js +19 -40
  140. package/dist/patterns.js.map +1 -1
  141. package/dist/specialization/type-aliases.test.js +8 -0
  142. package/dist/specialization/type-aliases.test.js.map +1 -1
  143. package/dist/statements/classes/members/methods.d.ts.map +1 -1
  144. package/dist/statements/classes/members/methods.js +5 -22
  145. package/dist/statements/classes/members/methods.js.map +1 -1
  146. package/dist/statements/classes/parameters.d.ts.map +1 -1
  147. package/dist/statements/classes/parameters.js +2 -1
  148. package/dist/statements/classes/parameters.js.map +1 -1
  149. package/dist/statements/classes/properties.d.ts.map +1 -1
  150. package/dist/statements/classes/properties.js +5 -16
  151. package/dist/statements/classes/properties.js.map +1 -1
  152. package/dist/statements/control/conditionals/guard-analysis.d.ts +44 -1
  153. package/dist/statements/control/conditionals/guard-analysis.d.ts.map +1 -1
  154. package/dist/statements/control/conditionals/guard-analysis.js +301 -125
  155. package/dist/statements/control/conditionals/guard-analysis.js.map +1 -1
  156. package/dist/statements/control/conditionals/if-emitter.d.ts.map +1 -1
  157. package/dist/statements/control/conditionals/if-emitter.js +182 -53
  158. package/dist/statements/control/conditionals/if-emitter.js.map +1 -1
  159. package/dist/statements/control/exceptions.d.ts.map +1 -1
  160. package/dist/statements/control/exceptions.js +2 -4
  161. package/dist/statements/control/exceptions.js.map +1 -1
  162. package/dist/statements/control/loops.d.ts.map +1 -1
  163. package/dist/statements/control/loops.js +3 -5
  164. package/dist/statements/control/loops.js.map +1 -1
  165. package/dist/statements/declarations/classes.d.ts.map +1 -1
  166. package/dist/statements/declarations/classes.js +2 -4
  167. package/dist/statements/declarations/classes.js.map +1 -1
  168. package/dist/statements/declarations/functions.d.ts.map +1 -1
  169. package/dist/statements/declarations/functions.js +38 -79
  170. package/dist/statements/declarations/functions.js.map +1 -1
  171. package/dist/statements/declarations/interfaces-mutable-storage.test.js +10 -2
  172. package/dist/statements/declarations/interfaces-mutable-storage.test.js.map +1 -1
  173. package/dist/statements/declarations/interfaces.d.ts.map +1 -1
  174. package/dist/statements/declarations/interfaces.js +4 -12
  175. package/dist/statements/declarations/interfaces.js.map +1 -1
  176. package/dist/statements/declarations/type-aliases.d.ts.map +1 -1
  177. package/dist/statements/declarations/type-aliases.js +5 -9
  178. package/dist/statements/declarations/type-aliases.js.map +1 -1
  179. package/dist/statements/declarations/variables.d.ts.map +1 -1
  180. package/dist/statements/declarations/variables.js +55 -51
  181. package/dist/statements/declarations/variables.js.map +1 -1
  182. package/dist/statements/index.test.js +1026 -0
  183. package/dist/statements/index.test.js.map +1 -1
  184. package/dist/types/dictionaries.d.ts.map +1 -1
  185. package/dist/types/dictionaries.js +5 -5
  186. package/dist/types/dictionaries.js.map +1 -1
  187. package/dist/types/functions.d.ts.map +1 -1
  188. package/dist/types/functions.js +5 -25
  189. package/dist/types/functions.js.map +1 -1
  190. package/dist/types/primitives.d.ts.map +1 -1
  191. package/dist/types/primitives.js.map +1 -1
  192. package/dist/types/references.d.ts.map +1 -1
  193. package/dist/types/references.js +65 -128
  194. package/dist/types/references.js.map +1 -1
  195. package/dist/types/references.test.js +170 -46
  196. package/dist/types/references.test.js.map +1 -1
  197. package/dist/types/tuples.d.ts.map +1 -1
  198. package/dist/types/tuples.js +7 -17
  199. package/dist/types/tuples.js.map +1 -1
  200. package/dist/types/unions.d.ts.map +1 -1
  201. package/dist/types/unions.js +2 -5
  202. package/dist/types/unions.js.map +1 -1
  203. package/package.json +2 -2
  204. package/dist/expressions/parentheses.d.ts +0 -4
  205. package/dist/expressions/parentheses.d.ts.map +0 -1
  206. package/dist/expressions/parentheses.js +0 -91
  207. package/dist/expressions/parentheses.js.map +0 -1
@@ -7,6 +7,7 @@
7
7
  */
8
8
  import { IrExpression, IrStatement, IrType } from "@tsonic/frontend";
9
9
  import { EmitterContext, NarrowedBinding } from "../../../types.js";
10
+ import type { CSharpTypeAst } from "../../../core/format/backend-ast/types.js";
10
11
  /**
11
12
  * Information extracted from a type predicate guard call.
12
13
  * Used to generate Union.IsN()/AsN() narrowing code.
@@ -16,6 +17,9 @@ export type GuardInfo = {
16
17
  readonly targetType: IrType;
17
18
  readonly memberN: number;
18
19
  readonly unionArity: number;
20
+ readonly runtimeUnionArity: number;
21
+ readonly candidateMemberNs: readonly number[];
22
+ readonly candidateMembers: readonly IrType[];
19
23
  readonly ctxWithId: EmitterContext;
20
24
  readonly narrowedName: string;
21
25
  readonly escapedOrig: string;
@@ -29,7 +33,7 @@ export type GuardInfo = {
29
33
  */
30
34
  export type InstanceofGuardInfo = {
31
35
  readonly originalName: string;
32
- readonly rhsTypeText: string;
36
+ readonly rhsTypeAst: CSharpTypeAst;
33
37
  readonly ctxWithId: EmitterContext;
34
38
  readonly ctxAfterRhs: EmitterContext;
35
39
  readonly narrowedName: string;
@@ -55,6 +59,9 @@ export type InGuardInfo = {
55
59
  readonly propertyName: string;
56
60
  readonly memberN: number;
57
61
  readonly unionArity: number;
62
+ readonly runtimeUnionArity: number;
63
+ readonly candidateMemberNs: readonly number[];
64
+ readonly candidateMembers: readonly IrType[];
58
65
  readonly ctxWithId: EmitterContext;
59
66
  readonly narrowedName: string;
60
67
  readonly escapedOrig: string;
@@ -83,12 +90,43 @@ export type DiscriminantEqualityGuardInfo = {
83
90
  readonly operator: "===" | "!==" | "==" | "!=";
84
91
  readonly memberN: number;
85
92
  readonly unionArity: number;
93
+ readonly runtimeUnionArity: number;
94
+ readonly candidateMemberNs: readonly number[];
95
+ readonly candidateMembers: readonly IrType[];
86
96
  readonly ctxWithId: EmitterContext;
87
97
  readonly narrowedName: string;
88
98
  readonly escapedOrig: string;
89
99
  readonly escapedNarrow: string;
90
100
  readonly narrowedMap: Map<string, NarrowedBinding>;
91
101
  };
102
+ /**
103
+ * Information extracted from a truthy/falsy property guard:
104
+ * if (result.success) { ... }
105
+ * if (!result.success) { ... }
106
+ *
107
+ * Supports the airplane-grade case where a union member property is definitively
108
+ * truthy or falsy by literal/nullish contract, and exactly one member matches the
109
+ * condition branch.
110
+ */
111
+ export type PropertyTruthinessGuardInfo = {
112
+ readonly originalName: string;
113
+ readonly propertyName: string;
114
+ readonly memberN: number;
115
+ readonly unionArity: number;
116
+ readonly runtimeUnionArity: number;
117
+ readonly candidateMemberNs: readonly number[];
118
+ readonly candidateMembers: readonly IrType[];
119
+ readonly ctxWithId: EmitterContext;
120
+ readonly narrowedName: string;
121
+ readonly escapedOrig: string;
122
+ readonly escapedNarrow: string;
123
+ readonly narrowedMap: Map<string, NarrowedBinding>;
124
+ };
125
+ export type RuntimeUnionFrame = {
126
+ readonly members: readonly IrType[];
127
+ readonly candidateMemberNs: readonly number[];
128
+ readonly runtimeUnionArity: number;
129
+ };
92
130
  /**
93
131
  * Information extracted from a nullable guard condition.
94
132
  * Used to generate .Value access for narrowed nullable value types.
@@ -109,6 +147,11 @@ export type NullableGuardInfo = {
109
147
  * TypeScript flow analysis, by mapping the literal to exactly one union member.
110
148
  */
111
149
  export declare const tryResolveDiscriminantEqualityGuard: (condition: IrExpression, context: EmitterContext) => DiscriminantEqualityGuardInfo | undefined;
150
+ /**
151
+ * Try to extract guard info from `x.prop` / `!x.prop` where the property acts as a
152
+ * boolean-style discriminant over a runtime union.
153
+ */
154
+ export declare const tryResolvePropertyTruthinessGuard: (condition: IrExpression, context: EmitterContext) => PropertyTruthinessGuardInfo | undefined;
112
155
  /**
113
156
  * Try to extract guard info from an `("prop" in x)` binary expression.
114
157
  */
@@ -1 +1 @@
1
- {"version":3,"file":"guard-analysis.d.ts","sourceRoot":"","sources":["../../../../src/statements/control/conditionals/guard-analysis.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"guard-analysis.d.ts","sourceRoot":"","sources":["../../../../src/statements/control/conditionals/guard-analysis.ts"],"names":[],"mappings":"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"}
@@ -7,77 +7,44 @@
7
7
  */
8
8
  import { emitExpressionAst } from "../../../expression-emitter.js";
9
9
  import { emitIdentifier } from "../../../expressions/identifiers.js";
10
+ import { emitTypeAst } from "../../../type-emitter.js";
10
11
  import { extractCalleeNameFromAst } from "../../../core/format/backend-ast/utils.js";
11
12
  /**
12
13
  * Extract the identifier string from an expression AST that is known to be
13
14
  * an identifierExpression. Falls back to extractCalleeNameFromAst for other shapes.
14
15
  */
15
16
  const extractIdentifierText = (ast) => extractCalleeNameFromAst(ast);
16
- import { resolveTypeAlias, stripNullish, findUnionMemberIndex, getPropertyType, getAllPropertySignatures, isDefinitelyValueType, } from "../../../core/semantic/type-resolution.js";
17
+ import { hasDeterministicPropertyMembership, resolveTypeAlias, stripNullish, findUnionMemberIndex, getPropertyType, isDefinitelyValueType, } from "../../../core/semantic/type-resolution.js";
17
18
  import { escapeCSharpIdentifier } from "../../../emitter-types/index.js";
18
19
  import { emitRemappedLocalName } from "../../../core/format/local-names.js";
19
20
  /**
20
21
  * Check if a local nominal type (class/interface) has a property with the given TS name.
21
22
  */
22
- const hasLocalProperty = (type, propertyName, context) => {
23
- if (!context.localTypes)
24
- return false;
25
- const info = context.localTypes.get(type.name);
26
- if (!info)
27
- return false;
28
- if (info.kind === "interface") {
29
- const props = getAllPropertySignatures(type, context);
30
- return props?.some((p) => p.name === propertyName) ?? false;
31
- }
32
- if (info.kind === "class") {
33
- return info.members.some((m) => m.kind === "propertyDeclaration" && m.name === propertyName);
23
+ const getGuardPropertyType = (type, propertyName, context) => {
24
+ if (type.kind === "objectType") {
25
+ const prop = type.members.find((member) => member.kind === "propertySignature" && member.name === propertyName);
26
+ return prop?.type;
34
27
  }
35
- return false;
36
- };
37
- /**
38
- * Check if a nominal type has a property, including cross-module local types.
39
- *
40
- * For same-module types, consult `context.localTypes`.
41
- * For cross-module types, consult the batch `typeMemberIndex` and resolve the
42
- * member's fully-qualified name deterministically.
43
- */
44
- const hasProperty = (type, propertyName, context) => {
45
- if (hasLocalProperty(type, propertyName, context)) {
46
- return true;
47
- }
48
- const index = context.options.typeMemberIndex;
49
- if (!index)
50
- return false;
51
- const stripGlobalPrefix = (name) => name.startsWith("global::") ? name.slice("global::".length) : name;
52
- const candidates = [];
53
- if (type.resolvedClrType) {
54
- candidates.push(stripGlobalPrefix(type.resolvedClrType));
55
- }
56
- else if (type.name.includes(".")) {
57
- candidates.push(type.name);
58
- }
59
- else {
60
- // Resolve by suffix match in the type member index.
61
- const matches = [];
62
- for (const fqn of index.keys()) {
63
- if (fqn.endsWith(`.${type.name}`) ||
64
- fqn.endsWith(`.${type.name}__Alias`)) {
65
- matches.push(fqn);
66
- }
28
+ if (type.kind === "referenceType") {
29
+ if (type.structuralMembers?.length) {
30
+ const prop = type.structuralMembers.find((member) => member.kind === "propertySignature" && member.name === propertyName);
31
+ if (prop)
32
+ return prop.type;
67
33
  }
68
- if (matches.length === 1) {
69
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
70
- candidates.push(matches[0]);
71
- }
72
- else if (matches.length > 1) {
73
- const list = matches.sort().join(", ");
74
- throw new Error(`ICE: Ambiguous union member type '${type.name}' for \`in\` narrowing. Candidates: ${list}`);
34
+ const localTypes = resolveLocalTypesForReference(type, context);
35
+ if (localTypes) {
36
+ const lookupName = type.name.includes(".")
37
+ ? (type.name.split(".").pop() ?? type.name)
38
+ : type.name;
39
+ const localPropType = getPropertyType({ ...type, name: lookupName }, propertyName, { ...context, localTypes });
40
+ if (localPropType)
41
+ return localPropType;
75
42
  }
43
+ const resolvedPropType = getPropertyType(type, propertyName, context);
44
+ if (resolvedPropType)
45
+ return resolvedPropType;
76
46
  }
77
- return candidates.some((fqn) => {
78
- const perType = index.get(fqn);
79
- return perType?.has(propertyName) ?? false;
80
- });
47
+ return undefined;
81
48
  };
82
49
  /**
83
50
  * Resolve a reference type's LocalTypeInfo map (possibly from a different module).
@@ -149,6 +116,90 @@ const tryGetLiteralSet = (type, context) => {
149
116
  }
150
117
  return undefined;
151
118
  };
119
+ const isClrUnionName = (name) => /^Union_[2-8]$/.test(name) || name === "Union" || name.endsWith(".Union");
120
+ const stripGlobalPrefix = (name) => name.startsWith("global::") ? name.slice("global::".length) : name;
121
+ const extractUnionMembers = (type, context) => {
122
+ const resolvedBase = resolveTypeAlias(stripNullish(type), context);
123
+ const resolved = resolvedBase.kind === "intersectionType"
124
+ ? (resolvedBase.types.find((member) => member.kind === "referenceType" && isClrUnionName(member.name)) ?? resolvedBase)
125
+ : resolvedBase;
126
+ if (resolved.kind === "unionType") {
127
+ return resolved.types;
128
+ }
129
+ if (resolved.kind === "referenceType" &&
130
+ isClrUnionName(resolved.name) &&
131
+ resolved.typeArguments &&
132
+ resolved.typeArguments.length >= 2 &&
133
+ resolved.typeArguments.length <= 8) {
134
+ return resolved.typeArguments;
135
+ }
136
+ return undefined;
137
+ };
138
+ const resolveRuntimeUnionFrame = (originalName, unionSourceType, context) => {
139
+ const members = extractUnionMembers(unionSourceType, context);
140
+ if (!members)
141
+ return undefined;
142
+ const narrowed = context.narrowedBindings?.get(originalName);
143
+ if (!narrowed) {
144
+ return {
145
+ members,
146
+ candidateMemberNs: members.map((_, index) => index + 1),
147
+ runtimeUnionArity: members.length,
148
+ };
149
+ }
150
+ if (narrowed.kind !== "runtimeSubset") {
151
+ return undefined;
152
+ }
153
+ if (narrowed.runtimeMemberNs.length !== members.length) {
154
+ return undefined;
155
+ }
156
+ return {
157
+ members,
158
+ candidateMemberNs: [...narrowed.runtimeMemberNs],
159
+ runtimeUnionArity: narrowed.runtimeUnionArity,
160
+ };
161
+ };
162
+ const buildRenameNarrowedMap = (originalName, narrowedName, memberType, ctxWithId) => {
163
+ const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
164
+ narrowedMap.set(originalName, {
165
+ kind: "rename",
166
+ name: narrowedName,
167
+ type: memberType,
168
+ });
169
+ return narrowedMap;
170
+ };
171
+ const isDefinitelyTruthyLiteral = (value) => {
172
+ if (typeof value === "string")
173
+ return value.length > 0;
174
+ if (typeof value === "number")
175
+ return value !== 0 && !Number.isNaN(value);
176
+ return value === true;
177
+ };
178
+ const isDefinitelyFalsyType = (type, context) => {
179
+ const resolved = resolveTypeAlias(type, context);
180
+ if (resolved.kind === "literalType") {
181
+ const value = resolved.value;
182
+ return value === false || value === 0 || value === "";
183
+ }
184
+ if (resolved.kind === "primitiveType") {
185
+ return resolved.name === "undefined" || resolved.name === "null";
186
+ }
187
+ if (resolved.kind === "unionType") {
188
+ return resolved.types.every((member) => isDefinitelyFalsyType(member, context));
189
+ }
190
+ return false;
191
+ };
192
+ const isDefinitelyTruthyType = (type, context) => {
193
+ const literals = tryGetLiteralSet(type, context);
194
+ if (literals) {
195
+ return Array.from(literals).every(isDefinitelyTruthyLiteral);
196
+ }
197
+ const resolved = resolveTypeAlias(type, context);
198
+ if (resolved.kind === "unionType") {
199
+ return resolved.types.every((member) => isDefinitelyTruthyType(member, context));
200
+ }
201
+ return false;
202
+ };
152
203
  /**
153
204
  * Try to extract guard info from `x.prop === <literal>` or `x.prop !== <literal>`.
154
205
  *
@@ -211,70 +262,52 @@ export const tryResolveDiscriminantEqualityGuard = (condition, context) => {
211
262
  return undefined;
212
263
  const { receiver, propertyName, literal } = match;
213
264
  const originalName = receiver.name;
214
- // If this identifier is already narrowed (union guard emitted earlier), do NOT try to
215
- // apply another union narrowing rule. This avoids mis-emitting `.IsN()` on a narrowed member type.
216
- if (context.narrowedBindings?.has(originalName))
217
- return undefined;
218
265
  const unionSourceType = receiver.inferredType;
219
266
  if (!unionSourceType)
220
267
  return undefined;
221
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
222
- if (resolved.kind !== "unionType")
268
+ const frame = resolveRuntimeUnionFrame(originalName, unionSourceType, context);
269
+ if (!frame)
223
270
  return undefined;
224
- const unionArity = resolved.types.length;
271
+ const { members, candidateMemberNs, runtimeUnionArity } = frame;
272
+ const unionArity = members.length;
225
273
  if (unionArity < 2 || unionArity > 8)
226
274
  return undefined;
227
275
  // Find which union members have a discriminant property type that includes the literal.
228
- const matchingMembers = [];
229
- for (let i = 0; i < resolved.types.length; i++) {
230
- const member = resolved.types[i];
276
+ const matchingIndices = [];
277
+ const matchingMemberNs = [];
278
+ for (let i = 0; i < members.length; i++) {
279
+ const member = members[i];
231
280
  if (!member)
232
281
  continue;
233
- let propType;
234
- if (member.kind === "objectType") {
235
- const prop = member.members.find((m) => m.kind === "propertySignature" && m.name === propertyName);
236
- propType = prop?.type;
237
- }
238
- else if (member.kind === "referenceType") {
239
- const localTypes = resolveLocalTypesForReference(member, context);
240
- if (!localTypes)
241
- continue;
242
- const lookupName = member.name.includes(".")
243
- ? (member.name.split(".").pop() ?? member.name)
244
- : member.name;
245
- // Use the target module's localTypes for property type resolution.
246
- propType = getPropertyType({ ...member, name: lookupName }, propertyName, { ...context, localTypes });
247
- }
248
- else {
249
- continue;
250
- }
282
+ const propType = getGuardPropertyType(member, propertyName, context);
251
283
  if (!propType)
252
284
  continue;
253
285
  const literals = tryGetLiteralSet(propType, context);
254
286
  if (!literals)
255
287
  continue;
256
288
  if (literals.has(literal)) {
257
- matchingMembers.push(i + 1);
289
+ matchingIndices.push(i);
290
+ matchingMemberNs.push(candidateMemberNs[i] ?? i + 1);
258
291
  }
259
292
  }
260
293
  // Only support the common airplane-grade case: exactly one matching member.
261
- if (matchingMembers.length !== 1)
294
+ if (matchingMemberNs.length !== 1)
262
295
  return undefined;
263
- const memberN = matchingMembers[0];
296
+ const memberN = matchingMemberNs[0];
264
297
  if (!memberN)
265
298
  return undefined;
299
+ const matchingIndex = matchingIndices[0];
300
+ if (matchingIndex === undefined)
301
+ return undefined;
266
302
  const nextId = (context.tempVarId ?? 0) + 1;
267
303
  const ctxWithId = { ...context, tempVarId: nextId };
268
304
  const narrowedName = `${originalName}__${memberN}_${nextId}`;
269
305
  const escapedOrig = emitRemappedLocalName(originalName, context);
270
306
  const escapedNarrow = escapeCSharpIdentifier(narrowedName);
271
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
272
- const memberType = resolved.types[memberN - 1];
273
- narrowedMap.set(originalName, {
274
- kind: "rename",
275
- name: narrowedName,
276
- type: memberType,
277
- });
307
+ const memberType = members[matchingIndex];
308
+ if (!memberType)
309
+ return undefined;
310
+ const narrowedMap = buildRenameNarrowedMap(originalName, narrowedName, memberType, ctxWithId);
278
311
  return {
279
312
  originalName,
280
313
  propertyName,
@@ -282,6 +315,129 @@ export const tryResolveDiscriminantEqualityGuard = (condition, context) => {
282
315
  operator: condition.operator,
283
316
  memberN,
284
317
  unionArity,
318
+ runtimeUnionArity,
319
+ candidateMemberNs,
320
+ candidateMembers: members,
321
+ ctxWithId,
322
+ narrowedName,
323
+ escapedOrig,
324
+ escapedNarrow,
325
+ narrowedMap,
326
+ };
327
+ };
328
+ /**
329
+ * Try to extract guard info from `x.prop` / `!x.prop` where the property acts as a
330
+ * boolean-style discriminant over a runtime union.
331
+ */
332
+ export const tryResolvePropertyTruthinessGuard = (condition, context) => {
333
+ const extract = (expr) => {
334
+ if (expr.kind === "unary" && expr.operator === "!") {
335
+ const inner = extract(expr.expression);
336
+ return inner ? { ...inner, wantTruthy: !inner.wantTruthy } : undefined;
337
+ }
338
+ if (expr.kind !== "memberAccess")
339
+ return undefined;
340
+ if (expr.isOptional || expr.isComputed)
341
+ return undefined;
342
+ if (expr.object.kind !== "identifier")
343
+ return undefined;
344
+ if (typeof expr.property !== "string")
345
+ return undefined;
346
+ return {
347
+ receiver: expr.object,
348
+ propertyName: expr.property,
349
+ wantTruthy: true,
350
+ bindingType: expr.memberBinding?.type,
351
+ bindingValueTruthiness: expr.inferredType?.kind === "literalType"
352
+ ? isDefinitelyTruthyLiteral(expr.inferredType.value)
353
+ : expr.inferredType?.kind === "primitiveType" &&
354
+ (expr.inferredType.name === "undefined" ||
355
+ expr.inferredType.name === "null")
356
+ ? false
357
+ : undefined,
358
+ };
359
+ };
360
+ const match = extract(condition);
361
+ if (!match)
362
+ return undefined;
363
+ const { receiver, propertyName, wantTruthy, bindingType, bindingValueTruthiness, } = match;
364
+ const originalName = receiver.name;
365
+ const unionSourceType = receiver.inferredType;
366
+ if (!unionSourceType)
367
+ return undefined;
368
+ const frame = resolveRuntimeUnionFrame(originalName, unionSourceType, context);
369
+ if (!frame)
370
+ return undefined;
371
+ const { members, candidateMemberNs, runtimeUnionArity } = frame;
372
+ const unionArity = members.length;
373
+ if (unionArity < 2 || unionArity > 8)
374
+ return undefined;
375
+ const matchingIndices = [];
376
+ const matchingMemberNs = [];
377
+ if (bindingType && bindingValueTruthiness !== undefined) {
378
+ const bindingTypeName = stripGlobalPrefix(bindingType);
379
+ const boundMemberIndex = members.findIndex((member) => {
380
+ if (member?.kind !== "referenceType")
381
+ return false;
382
+ const candidateClr = member.resolvedClrType
383
+ ? stripGlobalPrefix(member.resolvedClrType)
384
+ : undefined;
385
+ return (candidateClr === bindingTypeName || member.name === bindingTypeName);
386
+ });
387
+ if (boundMemberIndex >= 0) {
388
+ if (wantTruthy === bindingValueTruthiness) {
389
+ matchingIndices.push(boundMemberIndex);
390
+ matchingMemberNs.push(candidateMemberNs[boundMemberIndex] ?? boundMemberIndex + 1);
391
+ }
392
+ else if (unionArity === 2) {
393
+ const otherIndex = boundMemberIndex === 0 ? 1 : 0;
394
+ matchingIndices.push(otherIndex);
395
+ matchingMemberNs.push(candidateMemberNs[otherIndex] ?? otherIndex + 1);
396
+ }
397
+ }
398
+ }
399
+ if (matchingMemberNs.length === 0) {
400
+ for (let i = 0; i < members.length; i++) {
401
+ const member = members[i];
402
+ if (!member)
403
+ continue;
404
+ const propType = getGuardPropertyType(member, propertyName, context);
405
+ if (!propType)
406
+ continue;
407
+ const matches = wantTruthy
408
+ ? isDefinitelyTruthyType(propType, context)
409
+ : isDefinitelyFalsyType(propType, context);
410
+ if (matches) {
411
+ matchingIndices.push(i);
412
+ matchingMemberNs.push(candidateMemberNs[i] ?? i + 1);
413
+ }
414
+ }
415
+ }
416
+ if (matchingMemberNs.length !== 1)
417
+ return undefined;
418
+ const memberN = matchingMemberNs[0];
419
+ if (!memberN)
420
+ return undefined;
421
+ const matchingIndex = matchingIndices[0];
422
+ if (matchingIndex === undefined)
423
+ return undefined;
424
+ const nextId = (context.tempVarId ?? 0) + 1;
425
+ const ctxWithId = { ...context, tempVarId: nextId };
426
+ const narrowedName = `${originalName}__${memberN}_${nextId}`;
427
+ const escapedOrig = emitRemappedLocalName(originalName, context);
428
+ const escapedNarrow = escapeCSharpIdentifier(narrowedName);
429
+ const memberType = members[matchingIndex];
430
+ if (!memberType)
431
+ return undefined;
432
+ const narrowedMap = buildRenameNarrowedMap(originalName, narrowedName, memberType, ctxWithId);
433
+ return {
434
+ originalName,
435
+ propertyName,
436
+ memberN,
437
+ unionArity,
438
+ runtimeUnionArity,
439
+ candidateMemberNs,
440
+ candidateMembers: members,
285
441
  ctxWithId,
286
442
  narrowedName,
287
443
  escapedOrig,
@@ -310,46 +466,52 @@ export const tryResolveInGuard = (condition, context) => {
310
466
  const unionSourceType = condition.right.inferredType;
311
467
  if (!unionSourceType)
312
468
  return undefined;
313
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
314
- if (resolved.kind !== "unionType")
469
+ const frame = resolveRuntimeUnionFrame(originalName, unionSourceType, context);
470
+ if (!frame)
315
471
  return undefined;
316
- const unionArity = resolved.types.length;
472
+ const { members, candidateMemberNs, runtimeUnionArity } = frame;
473
+ const unionArity = members.length;
317
474
  if (unionArity < 2 || unionArity > 8)
318
475
  return undefined;
319
476
  // Find which union members contain the property.
320
- const matchingMembers = [];
321
- for (let i = 0; i < resolved.types.length; i++) {
322
- const member = resolved.types[i];
477
+ const matchingIndices = [];
478
+ const matchingMemberNs = [];
479
+ for (let i = 0; i < members.length; i++) {
480
+ const member = members[i];
323
481
  if (!member || member.kind !== "referenceType")
324
482
  continue;
325
- if (hasProperty(member, propertyName, context)) {
326
- matchingMembers.push(i + 1);
483
+ if (hasDeterministicPropertyMembership(member, propertyName, context) === true) {
484
+ matchingIndices.push(i);
485
+ matchingMemberNs.push(candidateMemberNs[i] ?? i + 1);
327
486
  }
328
487
  }
329
488
  // Only support the common "exactly one matching member" narrowing case.
330
- if (matchingMembers.length !== 1)
489
+ if (matchingMemberNs.length !== 1)
331
490
  return undefined;
332
- const memberN = matchingMembers[0];
491
+ const memberN = matchingMemberNs[0];
333
492
  if (!memberN)
334
493
  return undefined;
494
+ const matchingIndex = matchingIndices[0];
495
+ if (matchingIndex === undefined)
496
+ return undefined;
335
497
  const nextId = (context.tempVarId ?? 0) + 1;
336
498
  const ctxWithId = { ...context, tempVarId: nextId };
337
499
  const narrowedName = `${originalName}__${memberN}_${nextId}`;
338
500
  const [rhsAst] = emitIdentifier(condition.right, context);
339
501
  const escapedOrig = extractIdentifierText(rhsAst);
340
502
  const escapedNarrow = escapeCSharpIdentifier(narrowedName);
341
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
342
- const memberType = resolved.types[memberN - 1];
343
- narrowedMap.set(originalName, {
344
- kind: "rename",
345
- name: narrowedName,
346
- type: memberType,
347
- });
503
+ const memberType = members[matchingIndex];
504
+ if (!memberType)
505
+ return undefined;
506
+ const narrowedMap = buildRenameNarrowedMap(originalName, narrowedName, memberType, ctxWithId);
348
507
  return {
349
508
  originalName,
350
509
  propertyName,
351
510
  memberN,
352
511
  unionArity,
512
+ runtimeUnionArity,
513
+ candidateMemberNs,
514
+ candidateMembers: members,
353
515
  ctxWithId,
354
516
  narrowedName,
355
517
  escapedOrig,
@@ -395,31 +557,33 @@ export const tryResolvePredicateGuard = (call, context) => {
395
557
  const unionSourceType = arg.inferredType;
396
558
  if (!unionSourceType)
397
559
  return undefined;
398
- const resolved = resolveTypeAlias(stripNullish(unionSourceType), context);
399
- if (resolved.kind !== "unionType")
560
+ const frame = resolveRuntimeUnionFrame(originalName, unionSourceType, context);
561
+ if (!frame)
400
562
  return undefined;
563
+ const resolved = {
564
+ kind: "unionType",
565
+ types: [...frame.members],
566
+ };
401
567
  const idx = findUnionMemberIndex(resolved, narrowing.targetType, context);
402
568
  if (idx === undefined)
403
569
  return undefined;
404
- const memberN = idx + 1;
405
- const unionArity = resolved.types.length;
570
+ const memberN = frame.candidateMemberNs[idx] ?? idx + 1;
571
+ const unionArity = frame.members.length;
406
572
  const nextId = (context.tempVarId ?? 0) + 1;
407
573
  const ctxWithId = { ...context, tempVarId: nextId };
408
574
  const narrowedName = `${originalName}__${memberN}_${nextId}`;
409
575
  const [argAst] = emitIdentifier(arg, context);
410
576
  const escapedOrig = extractIdentifierText(argAst);
411
577
  const escapedNarrow = escapeCSharpIdentifier(narrowedName);
412
- const narrowedMap = new Map(ctxWithId.narrowedBindings ?? []);
413
- narrowedMap.set(originalName, {
414
- kind: "rename",
415
- name: narrowedName,
416
- type: narrowing.targetType,
417
- });
578
+ const narrowedMap = buildRenameNarrowedMap(originalName, narrowedName, narrowing.targetType, ctxWithId);
418
579
  return {
419
580
  originalName,
420
581
  targetType: narrowing.targetType,
421
582
  memberN,
422
583
  unionArity,
584
+ runtimeUnionArity: frame.runtimeUnionArity,
585
+ candidateMemberNs: frame.candidateMemberNs,
586
+ candidateMembers: frame.members,
423
587
  ctxWithId,
424
588
  narrowedName,
425
589
  escapedOrig,
@@ -447,9 +611,21 @@ export const tryResolveInstanceofGuard = (condition, context) => {
447
611
  const escapedOrig = extractIdentifierText(lhsAst);
448
612
  const nextId = (ctxAfterLhs.tempVarId ?? 0) + 1;
449
613
  const ctxWithId = { ...ctxAfterLhs, tempVarId: nextId };
450
- // Emit RHS as a type name (e.g., global::System.String)
451
- const [rhsAst, ctxAfterRhs] = emitExpressionAst(condition.right, ctxWithId);
452
- const rhsTypeText = extractIdentifierText(rhsAst);
614
+ const [rhsAst, rhsCtxAfterExpr] = emitExpressionAst(condition.right, ctxWithId);
615
+ const inferredRhsType = condition.right.inferredType;
616
+ let rhsTypeAst;
617
+ let ctxAfterRhs = rhsCtxAfterExpr;
618
+ if (rhsAst.kind === "typeReferenceExpression") {
619
+ rhsTypeAst = rhsAst.type;
620
+ }
621
+ else if (inferredRhsType) {
622
+ const [emittedTypeAst, nextCtx] = emitTypeAst(inferredRhsType, rhsCtxAfterExpr);
623
+ rhsTypeAst = emittedTypeAst;
624
+ ctxAfterRhs = nextCtx;
625
+ }
626
+ if (!rhsTypeAst) {
627
+ return undefined;
628
+ }
453
629
  // Pattern variable name for the narrowed value.
454
630
  const narrowedName = `${originalName}__is_${nextId}`;
455
631
  const escapedNarrow = escapeCSharpIdentifier(narrowedName);
@@ -461,7 +637,7 @@ export const tryResolveInstanceofGuard = (condition, context) => {
461
637
  });
462
638
  return {
463
639
  originalName,
464
- rhsTypeText,
640
+ rhsTypeAst,
465
641
  ctxWithId,
466
642
  ctxAfterRhs,
467
643
  narrowedName,