numtypes 0.0.0
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/CHANGELOG.md +6 -0
- package/LICENSE +12 -0
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +652 -0
- package/dist/lib/index.d.ts +22 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +2 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/transformer/analyze/analyze-source-file.d.ts +15 -0
- package/dist/transformer/analyze/analyze-source-file.d.ts.map +1 -0
- package/dist/transformer/analyze/analyze-source-file.js +605 -0
- package/dist/transformer/analyze/analyze-source-file.js.map +1 -0
- package/dist/transformer/analyze/get-contextual-domain.d.ts +19 -0
- package/dist/transformer/analyze/get-contextual-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/get-contextual-domain.js +197 -0
- package/dist/transformer/analyze/get-contextual-domain.js.map +1 -0
- package/dist/transformer/analyze/get-expression-domain.d.ts +26 -0
- package/dist/transformer/analyze/get-expression-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/get-expression-domain.js +804 -0
- package/dist/transformer/analyze/get-expression-domain.js.map +1 -0
- package/dist/transformer/analyze/type-domain.d.ts +41 -0
- package/dist/transformer/analyze/type-domain.d.ts.map +1 -0
- package/dist/transformer/analyze/type-domain.js +260 -0
- package/dist/transformer/analyze/type-domain.js.map +1 -0
- package/dist/transformer/ast.d.ts +10 -0
- package/dist/transformer/ast.d.ts.map +1 -0
- package/dist/transformer/ast.js +115 -0
- package/dist/transformer/ast.js.map +1 -0
- package/dist/transformer/diagnostics.d.ts +17 -0
- package/dist/transformer/diagnostics.d.ts.map +1 -0
- package/dist/transformer/diagnostics.js +30 -0
- package/dist/transformer/diagnostics.js.map +1 -0
- package/dist/transformer/domains.d.ts +11 -0
- package/dist/transformer/domains.d.ts.map +1 -0
- package/dist/transformer/domains.js +32 -0
- package/dist/transformer/domains.js.map +1 -0
- package/dist/transformer/index.d.ts +10 -0
- package/dist/transformer/index.d.ts.map +1 -0
- package/dist/transformer/index.js +60 -0
- package/dist/transformer/index.js.map +1 -0
- package/dist/transformer/operators.d.ts +16 -0
- package/dist/transformer/operators.d.ts.map +1 -0
- package/dist/transformer/operators.js +44 -0
- package/dist/transformer/operators.js.map +1 -0
- package/dist/transformer/options.d.ts +19 -0
- package/dist/transformer/options.d.ts.map +1 -0
- package/dist/transformer/options.js +17 -0
- package/dist/transformer/options.js.map +1 -0
- package/dist/transformer/symbols.d.ts +56 -0
- package/dist/transformer/symbols.d.ts.map +1 -0
- package/dist/transformer/symbols.js +270 -0
- package/dist/transformer/symbols.js.map +1 -0
- package/dist/transformer/transform/erase-imports.d.ts +14 -0
- package/dist/transformer/transform/erase-imports.d.ts.map +1 -0
- package/dist/transformer/transform/erase-imports.js +174 -0
- package/dist/transformer/transform/erase-imports.js.map +1 -0
- package/dist/transformer/transform/generated-coercions.d.ts +9 -0
- package/dist/transformer/transform/generated-coercions.d.ts.map +1 -0
- package/dist/transformer/transform/generated-coercions.js +22 -0
- package/dist/transformer/transform/generated-coercions.js.map +1 -0
- package/dist/transformer/transform/optimize-coercions.d.ts +11 -0
- package/dist/transformer/transform/optimize-coercions.d.ts.map +1 -0
- package/dist/transformer/transform/optimize-coercions.js +1702 -0
- package/dist/transformer/transform/optimize-coercions.js.map +1 -0
- package/dist/transformer/transform/transform-declaration-file.d.ts +9 -0
- package/dist/transformer/transform/transform-declaration-file.d.ts.map +1 -0
- package/dist/transformer/transform/transform-declaration-file.js +376 -0
- package/dist/transformer/transform/transform-declaration-file.js.map +1 -0
- package/dist/transformer/transform/transform-expression.d.ts +24 -0
- package/dist/transformer/transform/transform-expression.d.ts.map +1 -0
- package/dist/transformer/transform/transform-expression.js +545 -0
- package/dist/transformer/transform/transform-expression.js.map +1 -0
- package/dist/transformer/transform/transform-source-file.d.ts +10 -0
- package/dist/transformer/transform/transform-source-file.d.ts.map +1 -0
- package/dist/transformer/transform/transform-source-file.js +52 -0
- package/dist/transformer/transform/transform-source-file.js.map +1 -0
- package/dist/transformer/ts-compat.d.ts +4 -0
- package/dist/transformer/ts-compat.d.ts.map +1 -0
- package/dist/transformer/ts-compat.js +24 -0
- package/dist/transformer/ts-compat.js.map +1 -0
- package/docs/implementation-plan.md +335 -0
- package/docs/lib-implementation.md +77 -0
- package/docs/lowering-optimization-spec.md +1020 -0
- package/docs/project-structure.md +52 -0
- package/docs/transform-spec.md +2114 -0
- package/package.json +83 -0
|
@@ -0,0 +1,804 @@
|
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { isNumericTypeDomain } from "./type-domain.js";
|
|
3
|
+
import { createDiagnostic } from "../diagnostics.js";
|
|
4
|
+
import { getTransparentDomainWrapperExpression, isTransparentDomainWrapper } from "../ast.js";
|
|
5
|
+
import { sortNumericDomains } from "../domains.js";
|
|
6
|
+
import { supportsBitwiseOperator } from "../operators.js";
|
|
7
|
+
import { getTypeDomain } from "./type-domain.js";
|
|
8
|
+
import { getNumtypeBindingName } from "../symbols.js";
|
|
9
|
+
const unaryArithmeticOperatorBySyntaxKind = new Map([
|
|
10
|
+
[ts.SyntaxKind.PlusToken, "+"],
|
|
11
|
+
[ts.SyntaxKind.MinusToken, "-"]
|
|
12
|
+
]);
|
|
13
|
+
const binaryArithmeticOperatorBySyntaxKind = new Map([
|
|
14
|
+
[ts.SyntaxKind.PlusToken, "+"],
|
|
15
|
+
[ts.SyntaxKind.MinusToken, "-"],
|
|
16
|
+
[ts.SyntaxKind.AsteriskToken, "*"],
|
|
17
|
+
[ts.SyntaxKind.SlashToken, "/"],
|
|
18
|
+
[ts.SyntaxKind.PercentToken, "%"],
|
|
19
|
+
[ts.SyntaxKind.AsteriskAsteriskToken, "**"]
|
|
20
|
+
]);
|
|
21
|
+
const integerBitwiseOperatorBySyntaxKind = new Map([
|
|
22
|
+
[ts.SyntaxKind.AmpersandToken, "&"],
|
|
23
|
+
[ts.SyntaxKind.BarToken, "|"],
|
|
24
|
+
[ts.SyntaxKind.CaretToken, "^"],
|
|
25
|
+
[ts.SyntaxKind.LessThanLessThanToken, "<<"],
|
|
26
|
+
[ts.SyntaxKind.GreaterThanGreaterThanToken, ">>"],
|
|
27
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken, ">>>"]
|
|
28
|
+
]);
|
|
29
|
+
const compoundArithmeticOperatorBySyntaxKind = new Map([
|
|
30
|
+
[ts.SyntaxKind.PlusEqualsToken, "+"],
|
|
31
|
+
[ts.SyntaxKind.MinusEqualsToken, "-"],
|
|
32
|
+
[ts.SyntaxKind.AsteriskEqualsToken, "*"],
|
|
33
|
+
[ts.SyntaxKind.SlashEqualsToken, "/"],
|
|
34
|
+
[ts.SyntaxKind.PercentEqualsToken, "%"],
|
|
35
|
+
[ts.SyntaxKind.AsteriskAsteriskEqualsToken, "**"]
|
|
36
|
+
]);
|
|
37
|
+
const compoundBitwiseOperatorBySyntaxKind = new Map([
|
|
38
|
+
[ts.SyntaxKind.AmpersandEqualsToken, "&"],
|
|
39
|
+
[ts.SyntaxKind.BarEqualsToken, "|"],
|
|
40
|
+
[ts.SyntaxKind.CaretEqualsToken, "^"],
|
|
41
|
+
[ts.SyntaxKind.LessThanLessThanEqualsToken, "<<"],
|
|
42
|
+
[ts.SyntaxKind.GreaterThanGreaterThanEqualsToken, ">>"],
|
|
43
|
+
[ts.SyntaxKind.GreaterThanGreaterThanGreaterThanEqualsToken, ">>>"]
|
|
44
|
+
]);
|
|
45
|
+
export function createExpressionDomainCache() {
|
|
46
|
+
return new WeakMap();
|
|
47
|
+
}
|
|
48
|
+
export function getExpressionDomain(input) {
|
|
49
|
+
return analyzeExpression({
|
|
50
|
+
...input,
|
|
51
|
+
cache: input.cache ?? createExpressionDomainCache()
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
function analyzeExpression(input) {
|
|
55
|
+
const expression = skipParentheses(input.expression);
|
|
56
|
+
const cachedResult = getCachedResult(input.cache, expression, input.contextualDomain);
|
|
57
|
+
if (cachedResult !== undefined) {
|
|
58
|
+
return cachedResult;
|
|
59
|
+
}
|
|
60
|
+
const result = analyzeExpressionUncached({
|
|
61
|
+
...input,
|
|
62
|
+
expression
|
|
63
|
+
});
|
|
64
|
+
setCachedResult(input.cache, expression, input.contextualDomain, result);
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
function analyzeExpressionUncached(input) {
|
|
68
|
+
const expression = input.expression;
|
|
69
|
+
if (isContextualNumericLiteral(expression, input.contextualDomain)) {
|
|
70
|
+
return createExpressionResult({
|
|
71
|
+
diagnostics: [],
|
|
72
|
+
domain: input.contextualDomain,
|
|
73
|
+
reason: "contextual-literal",
|
|
74
|
+
requiresClosure: true
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (isNumericLiteralLike(expression)) {
|
|
78
|
+
return createExpressionResult({
|
|
79
|
+
diagnostics: [],
|
|
80
|
+
domain: "plain-number",
|
|
81
|
+
reason: "literal",
|
|
82
|
+
requiresClosure: false
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
if (isTransparentDomainWrapper(expression)) {
|
|
86
|
+
return analyzeExpression({
|
|
87
|
+
cache: input.cache,
|
|
88
|
+
checker: input.checker,
|
|
89
|
+
contextualDomain: input.contextualDomain,
|
|
90
|
+
expression: getTransparentDomainWrapperExpression(expression),
|
|
91
|
+
symbols: input.symbols
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
if (ts.isCallExpression(expression)) {
|
|
95
|
+
return analyzeCallExpression(input, expression);
|
|
96
|
+
}
|
|
97
|
+
if (ts.isPrefixUnaryExpression(expression)) {
|
|
98
|
+
const updateResult = analyzeUpdateExpression(input, expression);
|
|
99
|
+
if (updateResult !== undefined) {
|
|
100
|
+
return updateResult;
|
|
101
|
+
}
|
|
102
|
+
return analyzePrefixUnaryExpression(input, expression);
|
|
103
|
+
}
|
|
104
|
+
if (ts.isPostfixUnaryExpression(expression)) {
|
|
105
|
+
const updateResult = analyzeUpdateExpression(input, expression);
|
|
106
|
+
if (updateResult !== undefined) {
|
|
107
|
+
return updateResult;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ts.isBinaryExpression(expression)) {
|
|
111
|
+
return analyzeBinaryExpression(input, expression);
|
|
112
|
+
}
|
|
113
|
+
if (ts.isConditionalExpression(expression)) {
|
|
114
|
+
return analyzeConditionalExpression(input, expression);
|
|
115
|
+
}
|
|
116
|
+
return analyzeExpressionType(input, expression);
|
|
117
|
+
}
|
|
118
|
+
function analyzeUpdateExpression(input, expression) {
|
|
119
|
+
const operator = expression.operator;
|
|
120
|
+
if (!isUpdateOperator(operator)) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const operand = analyzeExpression({
|
|
124
|
+
cache: input.cache,
|
|
125
|
+
checker: input.checker,
|
|
126
|
+
expression: expression.operand,
|
|
127
|
+
symbols: input.symbols
|
|
128
|
+
});
|
|
129
|
+
const unionOperationDiagnostic = getUnsupportedUnionOperationDiagnostic(expression, getUpdateOperatorVerb(operator), operand);
|
|
130
|
+
if (unionOperationDiagnostic !== undefined) {
|
|
131
|
+
return createExpressionResult({
|
|
132
|
+
diagnostics: [...operand.diagnostics, unionOperationDiagnostic],
|
|
133
|
+
domain: "unknown",
|
|
134
|
+
hasNonNumericUnionMembers: operand.hasNonNumericUnionMembers,
|
|
135
|
+
reason: "unknown",
|
|
136
|
+
requiresClosure: false,
|
|
137
|
+
unionDomains: operand.unionDomains
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (!isNumericTypeDomain(operand.domain)) {
|
|
141
|
+
return createExpressionResult({
|
|
142
|
+
diagnostics: operand.diagnostics,
|
|
143
|
+
domain: operand.domain,
|
|
144
|
+
hasNonNumericUnionMembers: operand.hasNonNumericUnionMembers,
|
|
145
|
+
reason: operand.reason,
|
|
146
|
+
requiresClosure: false,
|
|
147
|
+
unionDomains: operand.unionDomains
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return createExpressionResult({
|
|
151
|
+
diagnostics: operand.diagnostics,
|
|
152
|
+
domain: operand.domain,
|
|
153
|
+
reason: "operator",
|
|
154
|
+
requiresClosure: true
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function analyzeCallExpression(input, expression) {
|
|
158
|
+
const binding = input.symbols.getBindingForCastCallee(expression.expression);
|
|
159
|
+
if (binding === undefined) {
|
|
160
|
+
return analyzeExpressionType(input, expression);
|
|
161
|
+
}
|
|
162
|
+
if (binding.isTypeOnly) {
|
|
163
|
+
return createExpressionResult({
|
|
164
|
+
diagnostics: [
|
|
165
|
+
createDiagnostic("NUMTYPES_UNKNOWN_SYMBOL", `cannot use type-only import '${getNumtypeBindingName(binding)}' as a numtypes cast function`, getDiagnosticLocation(expression.expression))
|
|
166
|
+
],
|
|
167
|
+
domain: "unknown",
|
|
168
|
+
reason: "unknown",
|
|
169
|
+
requiresClosure: false
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
const argumentDiagnostics = expression.arguments.flatMap((argument) => analyzeExpression({
|
|
173
|
+
cache: input.cache,
|
|
174
|
+
checker: input.checker,
|
|
175
|
+
expression: argument,
|
|
176
|
+
symbols: input.symbols
|
|
177
|
+
}).diagnostics);
|
|
178
|
+
const castCallDiagnostic = getCastCallMisuseDiagnostic(expression, binding);
|
|
179
|
+
if (castCallDiagnostic !== undefined) {
|
|
180
|
+
return createExpressionResult({
|
|
181
|
+
diagnostics: [...argumentDiagnostics, castCallDiagnostic],
|
|
182
|
+
domain: "unknown",
|
|
183
|
+
reason: "unknown",
|
|
184
|
+
requiresClosure: false
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
return createExpressionResult({
|
|
188
|
+
diagnostics: argumentDiagnostics,
|
|
189
|
+
domain: binding.domain,
|
|
190
|
+
reason: "cast-call",
|
|
191
|
+
requiresClosure: true
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function getCastCallMisuseDiagnostic(expression, binding) {
|
|
195
|
+
const bindingName = getNumtypeBindingName(binding);
|
|
196
|
+
if (expression.questionDotToken !== undefined ||
|
|
197
|
+
hasOptionalPropertyAccessCallee(expression.expression) ||
|
|
198
|
+
expression.typeArguments !== undefined) {
|
|
199
|
+
return createDiagnostic("NUMTYPES_ERASED_CAST_MISUSE", `numtypes cast function '${bindingName}' must be called directly as ${bindingName}(value)`, getDiagnosticLocation(expression.expression));
|
|
200
|
+
}
|
|
201
|
+
if (expression.arguments.length !== 1) {
|
|
202
|
+
return createDiagnostic("NUMTYPES_ERASED_CAST_MISUSE", `numtypes cast function '${bindingName}' must be called with exactly one argument`, getDiagnosticLocation(expression.expression));
|
|
203
|
+
}
|
|
204
|
+
const argument = expression.arguments[0];
|
|
205
|
+
if (argument !== undefined && ts.isSpreadElement(argument)) {
|
|
206
|
+
return createDiagnostic("NUMTYPES_ERASED_CAST_MISUSE", `numtypes cast function '${bindingName}' must be called directly as ${bindingName}(value)`, getDiagnosticLocation(expression.expression));
|
|
207
|
+
}
|
|
208
|
+
return undefined;
|
|
209
|
+
}
|
|
210
|
+
function hasOptionalPropertyAccessCallee(expression) {
|
|
211
|
+
return (ts.isPropertyAccessExpression(expression) &&
|
|
212
|
+
expression.questionDotToken !== undefined);
|
|
213
|
+
}
|
|
214
|
+
function analyzePrefixUnaryExpression(input, expression) {
|
|
215
|
+
const operator = getUnaryArithmeticOperator(expression.operator);
|
|
216
|
+
if (operator === undefined) {
|
|
217
|
+
return analyzeExpressionType(input, expression);
|
|
218
|
+
}
|
|
219
|
+
const operand = analyzeExpression({
|
|
220
|
+
cache: input.cache,
|
|
221
|
+
checker: input.checker,
|
|
222
|
+
expression: expression.operand,
|
|
223
|
+
symbols: input.symbols
|
|
224
|
+
});
|
|
225
|
+
const unionOperationDiagnostic = getUnsupportedUnionOperationDiagnostic(expression, getUnaryOperatorVerb(operator), operand);
|
|
226
|
+
if (unionOperationDiagnostic !== undefined) {
|
|
227
|
+
return createExpressionResult({
|
|
228
|
+
diagnostics: [...operand.diagnostics, unionOperationDiagnostic],
|
|
229
|
+
domain: "unknown",
|
|
230
|
+
hasNonNumericUnionMembers: operand.hasNonNumericUnionMembers,
|
|
231
|
+
reason: "unknown",
|
|
232
|
+
requiresClosure: false,
|
|
233
|
+
unionDomains: operand.unionDomains
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
if (!isNumericTypeDomain(operand.domain)) {
|
|
237
|
+
return createExpressionResult({
|
|
238
|
+
diagnostics: operand.diagnostics,
|
|
239
|
+
domain: operand.domain,
|
|
240
|
+
hasNonNumericUnionMembers: operand.hasNonNumericUnionMembers,
|
|
241
|
+
reason: operand.reason,
|
|
242
|
+
requiresClosure: false,
|
|
243
|
+
unionDomains: operand.unionDomains
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
return createExpressionResult({
|
|
247
|
+
diagnostics: operand.diagnostics,
|
|
248
|
+
domain: operand.domain,
|
|
249
|
+
reason: "operator",
|
|
250
|
+
requiresClosure: true
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
function analyzeBinaryExpression(input, expression) {
|
|
254
|
+
const arithmeticOperator = getBinaryArithmeticOperator(expression.operatorToken.kind);
|
|
255
|
+
if (arithmeticOperator !== undefined) {
|
|
256
|
+
return analyzeBinaryOperation(input, expression, {
|
|
257
|
+
kind: "binary-arithmetic",
|
|
258
|
+
operator: arithmeticOperator
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
const bitwiseOperator = getIntegerBitwiseOperator(expression.operatorToken.kind);
|
|
262
|
+
if (bitwiseOperator !== undefined) {
|
|
263
|
+
return analyzeBinaryOperation(input, expression, {
|
|
264
|
+
kind: "bitwise",
|
|
265
|
+
operator: bitwiseOperator
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
const compoundArithmeticOperator = getCompoundArithmeticOperator(expression.operatorToken.kind);
|
|
269
|
+
if (compoundArithmeticOperator !== undefined) {
|
|
270
|
+
return analyzeCompoundAssignmentExpression(input, expression);
|
|
271
|
+
}
|
|
272
|
+
const compoundBitwiseOperator = getCompoundBitwiseOperator(expression.operatorToken.kind);
|
|
273
|
+
if (compoundBitwiseOperator !== undefined) {
|
|
274
|
+
return analyzeCompoundAssignmentExpression(input, expression);
|
|
275
|
+
}
|
|
276
|
+
return analyzeExpressionType(input, expression);
|
|
277
|
+
}
|
|
278
|
+
function analyzeCompoundAssignmentExpression(input, expression) {
|
|
279
|
+
const left = analyzeExpression({
|
|
280
|
+
cache: input.cache,
|
|
281
|
+
checker: input.checker,
|
|
282
|
+
expression: expression.left,
|
|
283
|
+
symbols: input.symbols
|
|
284
|
+
});
|
|
285
|
+
const right = analyzeExpression({
|
|
286
|
+
cache: input.cache,
|
|
287
|
+
checker: input.checker,
|
|
288
|
+
expression: expression.right,
|
|
289
|
+
symbols: input.symbols
|
|
290
|
+
});
|
|
291
|
+
const unionDomains = getMergedExpressionResultDomains([left, right]);
|
|
292
|
+
const hasNonNumericUnionMembers = left.hasNonNumericUnionMembers === true ||
|
|
293
|
+
right.hasNonNumericUnionMembers === true;
|
|
294
|
+
if (!isNumericTypeDomain(left.domain) &&
|
|
295
|
+
!isNumericTypeDomain(right.domain) &&
|
|
296
|
+
unionDomains === undefined) {
|
|
297
|
+
return analyzeExpressionType(input, expression);
|
|
298
|
+
}
|
|
299
|
+
return createExpressionResult({
|
|
300
|
+
diagnostics: [
|
|
301
|
+
...left.diagnostics,
|
|
302
|
+
...right.diagnostics,
|
|
303
|
+
createDiagnostic("NUMTYPES_UNSUPPORTED_COMPOUND_ASSIGNMENT", `cannot use compound assignment '${getCompoundAssignmentText(expression.operatorToken.kind)}' with numtypes values; use an explicit checked assignment`, getDiagnosticLocation(expression))
|
|
304
|
+
],
|
|
305
|
+
domain: "unknown",
|
|
306
|
+
hasNonNumericUnionMembers,
|
|
307
|
+
reason: "unknown",
|
|
308
|
+
requiresClosure: false,
|
|
309
|
+
unionDomains
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
function analyzeConditionalExpression(input, expression) {
|
|
313
|
+
const condition = analyzeExpression({
|
|
314
|
+
cache: input.cache,
|
|
315
|
+
checker: input.checker,
|
|
316
|
+
expression: expression.condition,
|
|
317
|
+
symbols: input.symbols
|
|
318
|
+
});
|
|
319
|
+
const trueWithoutContext = analyzeExpression({
|
|
320
|
+
cache: input.cache,
|
|
321
|
+
checker: input.checker,
|
|
322
|
+
expression: expression.whenTrue,
|
|
323
|
+
symbols: input.symbols
|
|
324
|
+
});
|
|
325
|
+
const falseWithoutContext = analyzeExpression({
|
|
326
|
+
cache: input.cache,
|
|
327
|
+
checker: input.checker,
|
|
328
|
+
expression: expression.whenFalse,
|
|
329
|
+
symbols: input.symbols
|
|
330
|
+
});
|
|
331
|
+
const contextualDomain = input.contextualDomain ??
|
|
332
|
+
getConditionalBranchContextualDomain(trueWithoutContext.domain, falseWithoutContext.domain);
|
|
333
|
+
const whenTrue = analyzeExpression({
|
|
334
|
+
cache: input.cache,
|
|
335
|
+
checker: input.checker,
|
|
336
|
+
contextualDomain,
|
|
337
|
+
expression: expression.whenTrue,
|
|
338
|
+
symbols: input.symbols
|
|
339
|
+
});
|
|
340
|
+
const whenFalse = analyzeExpression({
|
|
341
|
+
cache: input.cache,
|
|
342
|
+
checker: input.checker,
|
|
343
|
+
contextualDomain,
|
|
344
|
+
expression: expression.whenFalse,
|
|
345
|
+
symbols: input.symbols
|
|
346
|
+
});
|
|
347
|
+
const numericUnion = getConditionalResultUnion(whenTrue, whenFalse);
|
|
348
|
+
const diagnostics = [
|
|
349
|
+
...condition.diagnostics,
|
|
350
|
+
...whenTrue.diagnostics,
|
|
351
|
+
...whenFalse.diagnostics
|
|
352
|
+
];
|
|
353
|
+
const domainError = input.contextualDomain === undefined
|
|
354
|
+
? getConditionalDomainErrorDiagnostic(expression, whenTrue, whenFalse)
|
|
355
|
+
: undefined;
|
|
356
|
+
if (domainError !== undefined) {
|
|
357
|
+
return createExpressionResult({
|
|
358
|
+
diagnostics: [...diagnostics, domainError],
|
|
359
|
+
domain: "unknown",
|
|
360
|
+
reason: "unknown",
|
|
361
|
+
requiresClosure: false,
|
|
362
|
+
hasNonNumericUnionMembers: numericUnion?.hasNonNumericMembers,
|
|
363
|
+
unionDomains: numericUnion?.domains
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
const domain = getConditionalResultDomain(whenTrue.domain, whenFalse.domain, contextualDomain);
|
|
367
|
+
return createExpressionResult({
|
|
368
|
+
diagnostics,
|
|
369
|
+
domain,
|
|
370
|
+
reason: domain === "unknown" ? "unknown" : "operator",
|
|
371
|
+
requiresClosure: isNumericTypeDomain(domain) || numericUnion !== undefined,
|
|
372
|
+
hasNonNumericUnionMembers: numericUnion?.hasNonNumericMembers,
|
|
373
|
+
unionDomains: numericUnion?.domains
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
function getConditionalDomainErrorDiagnostic(expression, whenTrue, whenFalse) {
|
|
377
|
+
if (isNumericTypeDomain(whenTrue.domain) && isNumericTypeDomain(whenFalse.domain)) {
|
|
378
|
+
if (whenTrue.domain !== whenFalse.domain) {
|
|
379
|
+
return undefined;
|
|
380
|
+
}
|
|
381
|
+
return undefined;
|
|
382
|
+
}
|
|
383
|
+
if (isNumericTypeDomain(whenTrue.domain) && whenFalse.domain === "plain-number") {
|
|
384
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot select ${whenTrue.domain} and number without explicit cast`, getDiagnosticLocation(expression));
|
|
385
|
+
}
|
|
386
|
+
if (whenTrue.domain === "plain-number" && isNumericTypeDomain(whenFalse.domain)) {
|
|
387
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot select number and ${whenFalse.domain} without explicit cast`, getDiagnosticLocation(expression));
|
|
388
|
+
}
|
|
389
|
+
const trueUnionCandidate = getExpressionNumericUnionCandidate(whenTrue);
|
|
390
|
+
const falseUnionCandidate = getExpressionNumericUnionCandidate(whenFalse);
|
|
391
|
+
if (trueUnionCandidate?.isUnion === true &&
|
|
392
|
+
whenFalse.domain === "plain-number") {
|
|
393
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot select union containing ${trueUnionCandidate.domains.join(" | ")} and number without explicit cast`, getDiagnosticLocation(expression));
|
|
394
|
+
}
|
|
395
|
+
if (whenTrue.domain === "plain-number" &&
|
|
396
|
+
falseUnionCandidate?.isUnion === true) {
|
|
397
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot select number and union containing ${falseUnionCandidate.domains.join(" | ")} without explicit cast`, getDiagnosticLocation(expression));
|
|
398
|
+
}
|
|
399
|
+
return undefined;
|
|
400
|
+
}
|
|
401
|
+
function getConditionalResultUnion(whenTrue, whenFalse) {
|
|
402
|
+
const trueCandidate = getExpressionNumericUnionCandidate(whenTrue);
|
|
403
|
+
const falseCandidate = getExpressionNumericUnionCandidate(whenFalse);
|
|
404
|
+
if (trueCandidate === undefined && falseCandidate === undefined) {
|
|
405
|
+
return undefined;
|
|
406
|
+
}
|
|
407
|
+
if (trueCandidate === undefined) {
|
|
408
|
+
if (whenTrue.domain === "plain-number") {
|
|
409
|
+
return undefined;
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
domains: falseCandidate?.domains ?? [],
|
|
413
|
+
hasNonNumericMembers: true,
|
|
414
|
+
isUnion: true
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
if (falseCandidate === undefined) {
|
|
418
|
+
if (whenFalse.domain === "plain-number") {
|
|
419
|
+
return undefined;
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
domains: trueCandidate.domains,
|
|
423
|
+
hasNonNumericMembers: true,
|
|
424
|
+
isUnion: true
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
const domains = new Set([
|
|
428
|
+
...trueCandidate.domains,
|
|
429
|
+
...falseCandidate.domains
|
|
430
|
+
]);
|
|
431
|
+
const hasNonNumericMembers = trueCandidate.hasNonNumericMembers ||
|
|
432
|
+
falseCandidate.hasNonNumericMembers;
|
|
433
|
+
const isUnion = trueCandidate.isUnion ||
|
|
434
|
+
falseCandidate.isUnion ||
|
|
435
|
+
domains.size > 1 ||
|
|
436
|
+
hasNonNumericMembers;
|
|
437
|
+
if (!isUnion) {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
return {
|
|
441
|
+
domains: sortNumericDomains(domains),
|
|
442
|
+
hasNonNumericMembers,
|
|
443
|
+
isUnion: true
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
function getExpressionNumericUnionCandidate(result) {
|
|
447
|
+
if (isNumericTypeDomain(result.domain)) {
|
|
448
|
+
return {
|
|
449
|
+
domains: [result.domain],
|
|
450
|
+
hasNonNumericMembers: false,
|
|
451
|
+
isUnion: false
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
if (result.unionDomains !== undefined && result.unionDomains.length > 0) {
|
|
455
|
+
return {
|
|
456
|
+
domains: result.unionDomains,
|
|
457
|
+
hasNonNumericMembers: result.hasNonNumericUnionMembers === true,
|
|
458
|
+
isUnion: true
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
function analyzeBinaryOperation(input, expression, operation) {
|
|
464
|
+
const leftWithoutContext = analyzeExpression({
|
|
465
|
+
cache: input.cache,
|
|
466
|
+
checker: input.checker,
|
|
467
|
+
expression: expression.left,
|
|
468
|
+
symbols: input.symbols
|
|
469
|
+
});
|
|
470
|
+
const rightWithoutContext = analyzeExpression({
|
|
471
|
+
cache: input.cache,
|
|
472
|
+
checker: input.checker,
|
|
473
|
+
expression: expression.right,
|
|
474
|
+
symbols: input.symbols
|
|
475
|
+
});
|
|
476
|
+
const contextualDomain = getBinaryContextualDomain(leftWithoutContext.domain, rightWithoutContext.domain);
|
|
477
|
+
const left = analyzeBinaryOperand(input, expression.left, contextualDomain);
|
|
478
|
+
const right = analyzeBinaryOperand(input, expression.right, contextualDomain);
|
|
479
|
+
const diagnostics = [
|
|
480
|
+
...left.diagnostics,
|
|
481
|
+
...right.diagnostics
|
|
482
|
+
];
|
|
483
|
+
const unionDomains = getMergedAnalyzedOperandDomains([left, right]);
|
|
484
|
+
const hasNonNumericUnionMembers = [left, right].some((operand) => operand.hasNonNumericUnionMembers === true);
|
|
485
|
+
const resultDomain = getBinaryResultDomain(left.domain, right.domain);
|
|
486
|
+
const domainError = getDomainErrorDiagnostic(expression, operation.operator, left, right);
|
|
487
|
+
if (domainError !== undefined) {
|
|
488
|
+
diagnostics.push(domainError);
|
|
489
|
+
return createExpressionResult({
|
|
490
|
+
diagnostics,
|
|
491
|
+
domain: "unknown",
|
|
492
|
+
hasNonNumericUnionMembers,
|
|
493
|
+
reason: "unknown",
|
|
494
|
+
requiresClosure: false,
|
|
495
|
+
unionDomains
|
|
496
|
+
});
|
|
497
|
+
}
|
|
498
|
+
if (resultDomain === "plain-number" || resultDomain === "unknown") {
|
|
499
|
+
return createExpressionResult({
|
|
500
|
+
diagnostics,
|
|
501
|
+
domain: resultDomain,
|
|
502
|
+
hasNonNumericUnionMembers,
|
|
503
|
+
reason: resultDomain === "plain-number" ? "type" : "unknown",
|
|
504
|
+
requiresClosure: false,
|
|
505
|
+
unionDomains
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (operation.kind === "bitwise" &&
|
|
509
|
+
!supportsBitwiseOperator(resultDomain, operation.operator)) {
|
|
510
|
+
diagnostics.push(createDiagnostic("NUMTYPES_BITWISE_FLOAT", `cannot apply bitwise operator '${operation.operator}' to ${resultDomain}`, getDiagnosticLocation(expression)));
|
|
511
|
+
return createExpressionResult({
|
|
512
|
+
diagnostics,
|
|
513
|
+
domain: "unknown",
|
|
514
|
+
hasNonNumericUnionMembers,
|
|
515
|
+
reason: "unknown",
|
|
516
|
+
requiresClosure: false,
|
|
517
|
+
unionDomains
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
return createExpressionResult({
|
|
521
|
+
diagnostics,
|
|
522
|
+
domain: resultDomain,
|
|
523
|
+
reason: "operator",
|
|
524
|
+
requiresClosure: true
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
function analyzeBinaryOperand(input, expression, contextualDomain) {
|
|
528
|
+
const result = analyzeExpression({
|
|
529
|
+
cache: input.cache,
|
|
530
|
+
checker: input.checker,
|
|
531
|
+
contextualDomain,
|
|
532
|
+
expression,
|
|
533
|
+
symbols: input.symbols
|
|
534
|
+
});
|
|
535
|
+
return {
|
|
536
|
+
diagnostics: result.diagnostics,
|
|
537
|
+
domain: result.domain,
|
|
538
|
+
expression,
|
|
539
|
+
hasNonNumericUnionMembers: result.hasNonNumericUnionMembers,
|
|
540
|
+
isContextualLiteral: result.reason === "contextual-literal",
|
|
541
|
+
unionDomains: result.unionDomains
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
function analyzeExpressionType(input, expression) {
|
|
545
|
+
const typeResult = getTypeDomain({
|
|
546
|
+
checker: input.checker,
|
|
547
|
+
node: expression,
|
|
548
|
+
symbols: input.symbols,
|
|
549
|
+
type: input.checker.getTypeAtLocation(expression)
|
|
550
|
+
});
|
|
551
|
+
return createExpressionResult({
|
|
552
|
+
diagnostics: typeResult.diagnostics,
|
|
553
|
+
domain: typeResult.domain,
|
|
554
|
+
hasNonNumericUnionMembers: typeResult.hasNonNumericUnionMembers,
|
|
555
|
+
reason: typeResult.domain === "unknown" ? "unknown" : "type",
|
|
556
|
+
requiresClosure: isNumericTypeDomain(typeResult.domain),
|
|
557
|
+
unionDomains: typeResult.unionDomains
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
function createExpressionResult(input) {
|
|
561
|
+
const result = {
|
|
562
|
+
diagnostics: input.diagnostics,
|
|
563
|
+
domain: input.domain,
|
|
564
|
+
isDomainBound: isNumericTypeDomain(input.domain) ||
|
|
565
|
+
(input.unionDomains !== undefined && input.unionDomains.length > 0),
|
|
566
|
+
reason: input.reason,
|
|
567
|
+
requiresClosure: input.requiresClosure
|
|
568
|
+
};
|
|
569
|
+
if (input.unionDomains !== undefined && input.unionDomains.length > 0) {
|
|
570
|
+
return {
|
|
571
|
+
...result,
|
|
572
|
+
hasNonNumericUnionMembers: input.hasNonNumericUnionMembers === true,
|
|
573
|
+
unionDomains: input.unionDomains
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
return result;
|
|
577
|
+
}
|
|
578
|
+
function getMergedAnalyzedOperandDomains(operands) {
|
|
579
|
+
const domains = new Set();
|
|
580
|
+
for (const operand of operands) {
|
|
581
|
+
for (const domain of operand.unionDomains ?? []) {
|
|
582
|
+
domains.add(domain);
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return domains.size === 0 ? undefined : sortNumericDomains(domains);
|
|
586
|
+
}
|
|
587
|
+
function getMergedExpressionResultDomains(results) {
|
|
588
|
+
const domains = new Set();
|
|
589
|
+
for (const result of results) {
|
|
590
|
+
if (isNumericTypeDomain(result.domain)) {
|
|
591
|
+
domains.add(result.domain);
|
|
592
|
+
}
|
|
593
|
+
for (const domain of result.unionDomains ?? []) {
|
|
594
|
+
domains.add(domain);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
return domains.size === 0 ? undefined : sortNumericDomains(domains);
|
|
598
|
+
}
|
|
599
|
+
function getBinaryContextualDomain(leftDomain, rightDomain) {
|
|
600
|
+
if (isNumericTypeDomain(leftDomain) && rightDomain === "plain-number") {
|
|
601
|
+
return leftDomain;
|
|
602
|
+
}
|
|
603
|
+
if (isNumericTypeDomain(rightDomain) && leftDomain === "plain-number") {
|
|
604
|
+
return rightDomain;
|
|
605
|
+
}
|
|
606
|
+
return undefined;
|
|
607
|
+
}
|
|
608
|
+
function getConditionalBranchContextualDomain(trueDomain, falseDomain) {
|
|
609
|
+
if (isNumericTypeDomain(trueDomain) && falseDomain === "plain-number") {
|
|
610
|
+
return trueDomain;
|
|
611
|
+
}
|
|
612
|
+
if (isNumericTypeDomain(falseDomain) && trueDomain === "plain-number") {
|
|
613
|
+
return falseDomain;
|
|
614
|
+
}
|
|
615
|
+
return undefined;
|
|
616
|
+
}
|
|
617
|
+
function getBinaryResultDomain(leftDomain, rightDomain) {
|
|
618
|
+
if (isNumericTypeDomain(leftDomain) && isNumericTypeDomain(rightDomain)) {
|
|
619
|
+
return leftDomain === rightDomain ? leftDomain : "unknown";
|
|
620
|
+
}
|
|
621
|
+
if (leftDomain === "plain-number" && rightDomain === "plain-number") {
|
|
622
|
+
return "plain-number";
|
|
623
|
+
}
|
|
624
|
+
if (isNumericTypeDomain(leftDomain) && rightDomain === "plain-number") {
|
|
625
|
+
return leftDomain;
|
|
626
|
+
}
|
|
627
|
+
if (leftDomain === "plain-number" && isNumericTypeDomain(rightDomain)) {
|
|
628
|
+
return rightDomain;
|
|
629
|
+
}
|
|
630
|
+
return "unknown";
|
|
631
|
+
}
|
|
632
|
+
function getConditionalResultDomain(trueDomain, falseDomain, contextualDomain) {
|
|
633
|
+
if (contextualDomain !== undefined &&
|
|
634
|
+
trueDomain === contextualDomain &&
|
|
635
|
+
falseDomain === contextualDomain) {
|
|
636
|
+
return contextualDomain;
|
|
637
|
+
}
|
|
638
|
+
if (isNumericTypeDomain(trueDomain) && isNumericTypeDomain(falseDomain)) {
|
|
639
|
+
return trueDomain === falseDomain ? trueDomain : "unknown";
|
|
640
|
+
}
|
|
641
|
+
if (trueDomain === "plain-number" && falseDomain === "plain-number") {
|
|
642
|
+
return "plain-number";
|
|
643
|
+
}
|
|
644
|
+
return "unknown";
|
|
645
|
+
}
|
|
646
|
+
function getCachedResult(cache, expression, contextualDomain) {
|
|
647
|
+
return cache.get(expression)?.get(getCacheKey(contextualDomain));
|
|
648
|
+
}
|
|
649
|
+
function setCachedResult(cache, expression, contextualDomain, result) {
|
|
650
|
+
const cacheKey = getCacheKey(contextualDomain);
|
|
651
|
+
const expressionCache = cache.get(expression) ?? new Map();
|
|
652
|
+
expressionCache.set(cacheKey, result);
|
|
653
|
+
cache.set(expression, expressionCache);
|
|
654
|
+
}
|
|
655
|
+
function getCacheKey(contextualDomain) {
|
|
656
|
+
return contextualDomain ?? "";
|
|
657
|
+
}
|
|
658
|
+
function getDomainErrorDiagnostic(expression, operator, left, right) {
|
|
659
|
+
const unionDomains = getMergedAnalyzedOperandDomains([left, right]);
|
|
660
|
+
if (unionDomains !== undefined) {
|
|
661
|
+
return createDiagnostic("NUMTYPES_UNSUPPORTED_UNION", `cannot ${getOperatorVerb(operator)} union containing ${unionDomains.join(" | ")}; narrow or explicitly cast before the operation`, getDiagnosticLocation(expression));
|
|
662
|
+
}
|
|
663
|
+
const leftDomain = left.domain;
|
|
664
|
+
const rightDomain = right.domain;
|
|
665
|
+
if (isNumericTypeDomain(leftDomain) && isNumericTypeDomain(rightDomain)) {
|
|
666
|
+
if (leftDomain !== rightDomain) {
|
|
667
|
+
return createDiagnostic("NUMTYPES_MIXED_DOMAIN", `cannot ${getOperatorVerb(operator)} ${leftDomain} and ${rightDomain} without explicit cast`, getDiagnosticLocation(expression));
|
|
668
|
+
}
|
|
669
|
+
return undefined;
|
|
670
|
+
}
|
|
671
|
+
if (isNumericTypeDomain(leftDomain) &&
|
|
672
|
+
rightDomain === "plain-number" &&
|
|
673
|
+
!right.isContextualLiteral) {
|
|
674
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot ${getOperatorVerb(operator)} ${leftDomain} and number without explicit cast`, getDiagnosticLocation(expression));
|
|
675
|
+
}
|
|
676
|
+
if (leftDomain === "plain-number" &&
|
|
677
|
+
isNumericTypeDomain(rightDomain) &&
|
|
678
|
+
!left.isContextualLiteral) {
|
|
679
|
+
return createDiagnostic("NUMTYPES_IMPLICIT_NUMBER", `cannot ${getOperatorVerb(operator)} number and ${rightDomain} without explicit cast`, getDiagnosticLocation(expression));
|
|
680
|
+
}
|
|
681
|
+
return undefined;
|
|
682
|
+
}
|
|
683
|
+
function isContextualNumericLiteral(expression, contextualDomain) {
|
|
684
|
+
if (contextualDomain === undefined || !isNumericLiteralLike(expression)) {
|
|
685
|
+
return false;
|
|
686
|
+
}
|
|
687
|
+
const numericValue = getNumericLiteralValue(expression);
|
|
688
|
+
if (contextualDomain === "f32" || contextualDomain === "f64") {
|
|
689
|
+
return true;
|
|
690
|
+
}
|
|
691
|
+
if (!Number.isInteger(numericValue)) {
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
if (contextualDomain === "u32" && numericValue < 0) {
|
|
695
|
+
return false;
|
|
696
|
+
}
|
|
697
|
+
return true;
|
|
698
|
+
}
|
|
699
|
+
function isNumericLiteralLike(expression) {
|
|
700
|
+
if (ts.isNumericLiteral(expression)) {
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
return (ts.isPrefixUnaryExpression(expression) &&
|
|
704
|
+
(expression.operator === ts.SyntaxKind.MinusToken ||
|
|
705
|
+
expression.operator === ts.SyntaxKind.PlusToken) &&
|
|
706
|
+
ts.isNumericLiteral(expression.operand));
|
|
707
|
+
}
|
|
708
|
+
function getNumericLiteralValue(expression) {
|
|
709
|
+
if (ts.isNumericLiteral(expression)) {
|
|
710
|
+
return Number(expression.text);
|
|
711
|
+
}
|
|
712
|
+
if (ts.isPrefixUnaryExpression(expression) &&
|
|
713
|
+
ts.isNumericLiteral(expression.operand)) {
|
|
714
|
+
const value = Number(expression.operand.text);
|
|
715
|
+
return expression.operator === ts.SyntaxKind.MinusToken ? -value : value;
|
|
716
|
+
}
|
|
717
|
+
return Number.NaN;
|
|
718
|
+
}
|
|
719
|
+
function getUnaryArithmeticOperator(operator) {
|
|
720
|
+
return unaryArithmeticOperatorBySyntaxKind.get(operator);
|
|
721
|
+
}
|
|
722
|
+
function getBinaryArithmeticOperator(operator) {
|
|
723
|
+
return binaryArithmeticOperatorBySyntaxKind.get(operator);
|
|
724
|
+
}
|
|
725
|
+
function getIntegerBitwiseOperator(operator) {
|
|
726
|
+
return integerBitwiseOperatorBySyntaxKind.get(operator);
|
|
727
|
+
}
|
|
728
|
+
function getCompoundArithmeticOperator(operator) {
|
|
729
|
+
return compoundArithmeticOperatorBySyntaxKind.get(operator);
|
|
730
|
+
}
|
|
731
|
+
function getCompoundBitwiseOperator(operator) {
|
|
732
|
+
return compoundBitwiseOperatorBySyntaxKind.get(operator);
|
|
733
|
+
}
|
|
734
|
+
function getCompoundAssignmentText(operator) {
|
|
735
|
+
return ts.tokenToString(operator) ?? "<unknown>";
|
|
736
|
+
}
|
|
737
|
+
function getOperatorVerb(operator) {
|
|
738
|
+
switch (operator) {
|
|
739
|
+
case "+":
|
|
740
|
+
return "add";
|
|
741
|
+
case "-":
|
|
742
|
+
return "subtract";
|
|
743
|
+
case "*":
|
|
744
|
+
return "multiply";
|
|
745
|
+
case "/":
|
|
746
|
+
return "divide";
|
|
747
|
+
case "%":
|
|
748
|
+
return "remainder";
|
|
749
|
+
case "**":
|
|
750
|
+
return "exponentiate";
|
|
751
|
+
case "&":
|
|
752
|
+
case "|":
|
|
753
|
+
case "^":
|
|
754
|
+
case "<<":
|
|
755
|
+
case ">>":
|
|
756
|
+
case ">>>":
|
|
757
|
+
return `apply operator '${operator}' to`;
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
function getUnaryOperatorVerb(operator) {
|
|
761
|
+
switch (operator) {
|
|
762
|
+
case "+":
|
|
763
|
+
return "apply unary '+' to";
|
|
764
|
+
case "-":
|
|
765
|
+
return "negate";
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
function getUpdateOperatorVerb(operator) {
|
|
769
|
+
switch (operator) {
|
|
770
|
+
case ts.SyntaxKind.PlusPlusToken:
|
|
771
|
+
return "increment";
|
|
772
|
+
case ts.SyntaxKind.MinusMinusToken:
|
|
773
|
+
return "decrement";
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function isUpdateOperator(operator) {
|
|
777
|
+
return (operator === ts.SyntaxKind.PlusPlusToken ||
|
|
778
|
+
operator === ts.SyntaxKind.MinusMinusToken);
|
|
779
|
+
}
|
|
780
|
+
function getUnsupportedUnionOperationDiagnostic(expression, operationVerb, operand) {
|
|
781
|
+
if (operand.unionDomains !== undefined && operand.unionDomains.length > 0) {
|
|
782
|
+
return createDiagnostic("NUMTYPES_UNSUPPORTED_UNION", `cannot ${operationVerb} union containing ${operand.unionDomains.join(" | ")}; narrow or explicitly cast before the operation`, getDiagnosticLocation(expression));
|
|
783
|
+
}
|
|
784
|
+
return undefined;
|
|
785
|
+
}
|
|
786
|
+
function skipParentheses(expression) {
|
|
787
|
+
let current = expression;
|
|
788
|
+
while (ts.isParenthesizedExpression(current)) {
|
|
789
|
+
current = current.expression;
|
|
790
|
+
}
|
|
791
|
+
return current;
|
|
792
|
+
}
|
|
793
|
+
function getDiagnosticLocation(node) {
|
|
794
|
+
if (node === undefined) {
|
|
795
|
+
return undefined;
|
|
796
|
+
}
|
|
797
|
+
const sourceFile = node.getSourceFile();
|
|
798
|
+
return {
|
|
799
|
+
fileName: sourceFile.fileName,
|
|
800
|
+
length: node.getWidth(sourceFile),
|
|
801
|
+
start: node.getStart(sourceFile)
|
|
802
|
+
};
|
|
803
|
+
}
|
|
804
|
+
//# sourceMappingURL=get-expression-domain.js.map
|