@typespec/compiler 0.56.0 → 0.57.0-dev.4
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/generated-defs/TypeSpec.d.ts +9 -9
- package/dist/generated-defs/TypeSpec.d.ts.map +1 -1
- package/dist/manifest.js +2 -2
- package/dist/src/core/binder.d.ts.map +1 -1
- package/dist/src/core/binder.js +15 -2
- package/dist/src/core/binder.js.map +1 -1
- package/dist/src/core/checker.d.ts +10 -8
- package/dist/src/core/checker.d.ts.map +1 -1
- package/dist/src/core/checker.js +1576 -241
- package/dist/src/core/checker.js.map +1 -1
- package/dist/src/core/compiler-code-fixes/model-to-object-literal.codefix.d.ts +6 -0
- package/dist/src/core/compiler-code-fixes/model-to-object-literal.codefix.d.ts.map +1 -0
- package/dist/src/core/compiler-code-fixes/model-to-object-literal.codefix.js +15 -0
- package/dist/src/core/compiler-code-fixes/model-to-object-literal.codefix.js.map +1 -0
- package/dist/src/core/compiler-code-fixes/tuple-to-array-value.codefix.d.ts +6 -0
- package/dist/src/core/compiler-code-fixes/tuple-to-array-value.codefix.d.ts.map +1 -0
- package/dist/src/core/compiler-code-fixes/tuple-to-array-value.codefix.js +15 -0
- package/dist/src/core/compiler-code-fixes/tuple-to-array-value.codefix.js.map +1 -0
- package/dist/src/core/diagnostic-creator.d.ts.map +1 -1
- package/dist/src/core/diagnostic-creator.js +1 -2
- package/dist/src/core/diagnostic-creator.js.map +1 -1
- package/dist/src/core/diagnostics.js +2 -2
- package/dist/src/core/diagnostics.js.map +1 -1
- package/dist/src/core/helpers/index.d.ts +1 -1
- package/dist/src/core/helpers/index.d.ts.map +1 -1
- package/dist/src/core/helpers/index.js +3 -1
- package/dist/src/core/helpers/index.js.map +1 -1
- package/dist/src/core/helpers/string-template-utils.d.ts +6 -6
- package/dist/src/core/helpers/string-template-utils.d.ts.map +1 -1
- package/dist/src/core/helpers/string-template-utils.js +20 -33
- package/dist/src/core/helpers/string-template-utils.js.map +1 -1
- package/dist/src/core/helpers/type-name-utils.d.ts +3 -2
- package/dist/src/core/helpers/type-name-utils.d.ts.map +1 -1
- package/dist/src/core/helpers/type-name-utils.js +57 -8
- package/dist/src/core/helpers/type-name-utils.js.map +1 -1
- package/dist/src/core/index.d.ts +3 -1
- package/dist/src/core/index.d.ts.map +1 -1
- package/dist/src/core/index.js +3 -1
- package/dist/src/core/index.js.map +1 -1
- package/dist/src/core/intrinsic-type-state.d.ts +57 -0
- package/dist/src/core/intrinsic-type-state.d.ts.map +1 -0
- package/dist/src/core/intrinsic-type-state.js +150 -0
- package/dist/src/core/intrinsic-type-state.js.map +1 -0
- package/dist/src/core/js-marshaller.d.ts +13 -0
- package/dist/src/core/js-marshaller.d.ts.map +1 -0
- package/dist/src/core/js-marshaller.js +79 -0
- package/dist/src/core/js-marshaller.js.map +1 -0
- package/dist/src/core/library.d.ts +2 -1
- package/dist/src/core/library.d.ts.map +1 -1
- package/dist/src/core/library.js +3 -0
- package/dist/src/core/library.js.map +1 -1
- package/dist/src/core/messages.d.ts +235 -2
- package/dist/src/core/messages.d.ts.map +1 -1
- package/dist/src/core/messages.js +74 -1
- package/dist/src/core/messages.js.map +1 -1
- package/dist/src/core/numeric-ranges.d.ts +51 -0
- package/dist/src/core/numeric-ranges.d.ts.map +1 -0
- package/dist/src/core/numeric-ranges.js +30 -0
- package/dist/src/core/numeric-ranges.js.map +1 -0
- package/dist/src/core/parser.d.ts.map +1 -1
- package/dist/src/core/parser.js +232 -5
- package/dist/src/core/parser.js.map +1 -1
- package/dist/src/core/program.d.ts.map +1 -1
- package/dist/src/core/program.js +2 -2
- package/dist/src/core/program.js.map +1 -1
- package/dist/src/core/projector.d.ts.map +1 -1
- package/dist/src/core/projector.js +10 -2
- package/dist/src/core/projector.js.map +1 -1
- package/dist/src/core/scanner.d.ts +42 -37
- package/dist/src/core/scanner.d.ts.map +1 -1
- package/dist/src/core/scanner.js +67 -46
- package/dist/src/core/scanner.js.map +1 -1
- package/dist/src/core/semantic-walker.d.ts.map +1 -1
- package/dist/src/core/semantic-walker.js +12 -0
- package/dist/src/core/semantic-walker.js.map +1 -1
- package/dist/src/core/type-utils.d.ts +18 -7
- package/dist/src/core/type-utils.d.ts.map +1 -1
- package/dist/src/core/type-utils.js +24 -5
- package/dist/src/core/type-utils.js.map +1 -1
- package/dist/src/core/types.d.ts +224 -45
- package/dist/src/core/types.d.ts.map +1 -1
- package/dist/src/core/types.js +8 -0
- package/dist/src/core/types.js.map +1 -1
- package/dist/src/emitter-framework/type-emitter.d.ts.map +1 -1
- package/dist/src/emitter-framework/type-emitter.js +6 -0
- package/dist/src/emitter-framework/type-emitter.js.map +1 -1
- package/dist/src/formatter/print/comment-handler.d.ts.map +1 -1
- package/dist/src/formatter/print/comment-handler.js +22 -0
- package/dist/src/formatter/print/comment-handler.js.map +1 -1
- package/dist/src/formatter/print/printer.d.ts +11 -5
- package/dist/src/formatter/print/printer.d.ts.map +1 -1
- package/dist/src/formatter/print/printer.js +95 -4
- package/dist/src/formatter/print/printer.js.map +1 -1
- package/dist/src/lib/decorators.d.ts +3 -46
- package/dist/src/lib/decorators.d.ts.map +1 -1
- package/dist/src/lib/decorators.js +28 -104
- package/dist/src/lib/decorators.js.map +1 -1
- package/dist/src/lib/intrinsic-decorators.d.ts +2 -2
- package/dist/src/lib/intrinsic-decorators.d.ts.map +1 -1
- package/dist/src/lib/intrinsic-decorators.js +7 -0
- package/dist/src/lib/intrinsic-decorators.js.map +1 -1
- package/dist/src/server/classify.d.ts.map +1 -1
- package/dist/src/server/classify.js +10 -0
- package/dist/src/server/classify.js.map +1 -1
- package/dist/src/server/completion.d.ts.map +1 -1
- package/dist/src/server/completion.js +6 -0
- package/dist/src/server/completion.js.map +1 -1
- package/dist/src/server/serverlib.js +2 -2
- package/dist/src/server/serverlib.js.map +1 -1
- package/dist/src/server/tmlanguage.d.ts +1 -1
- package/dist/src/server/tmlanguage.d.ts.map +1 -1
- package/dist/src/server/tmlanguage.js +147 -20
- package/dist/src/server/tmlanguage.js.map +1 -1
- package/dist/src/server/type-details.js +1 -2
- package/dist/src/server/type-details.js.map +1 -1
- package/dist/src/server/type-signature.js +15 -6
- package/dist/src/server/type-signature.js.map +1 -1
- package/dist/typespec.tmLanguage +408 -17
- package/lib/intrinsics.tsp +55 -5
- package/package.json +5 -4
package/dist/src/core/checker.js
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
-
import { $docFromComment,
|
|
2
|
-
import { getIndexer } from "../lib/intrinsic-decorators.js";
|
|
1
|
+
import { $docFromComment, getIndexer } from "../lib/intrinsic-decorators.js";
|
|
3
2
|
import { MultiKeyMap, createRekeyableMap, isArray, mutate } from "../utils/misc.js";
|
|
4
3
|
import { createSymbol, createSymbolTable } from "./binder.js";
|
|
5
4
|
import { createChangeIdentifierCodeFix } from "./compiler-code-fixes/change-identifier.codefix.js";
|
|
5
|
+
import { createModelToObjectValueCodeFix } from "./compiler-code-fixes/model-to-object-literal.codefix.js";
|
|
6
|
+
import { createTupleToArrayValueCodeFix } from "./compiler-code-fixes/tuple-to-array-value.codefix.js";
|
|
6
7
|
import { getDeprecationDetails, markDeprecated } from "./deprecation.js";
|
|
7
|
-
import { ProjectionError, compilerAssert, reportDeprecated } from "./diagnostics.js";
|
|
8
|
+
import { ProjectionError, compilerAssert, ignoreDiagnostics, reportDeprecated, } from "./diagnostics.js";
|
|
8
9
|
import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-utils.js";
|
|
9
|
-
import { getNamespaceFullName, getTypeName,
|
|
10
|
-
import {
|
|
10
|
+
import { getEntityName, getLocationContext, getNamespaceFullName, getTypeName, } from "./helpers/index.js";
|
|
11
|
+
import { explainStringTemplateNotSerializable } from "./helpers/string-template-utils.js";
|
|
12
|
+
import { getMaxItems, getMaxLength, getMaxValueAsNumeric, getMaxValueExclusiveAsNumeric, getMinItems, getMinLength, getMinValueAsNumeric, getMinValueExclusiveAsNumeric, } from "./intrinsic-type-state.js";
|
|
13
|
+
import { canNumericConstraintBeJsNumber, legacyMarshallTypeForJS, marshallTypeForJS, } from "./js-marshaller.js";
|
|
11
14
|
import { createDiagnostic } from "./messages.js";
|
|
15
|
+
import { numericRanges } from "./numeric-ranges.js";
|
|
16
|
+
import { Numeric } from "./numeric.js";
|
|
12
17
|
import { exprIsBareIdentifier, getIdentifierContext, hasParseError, visitChildren, } from "./parser.js";
|
|
13
18
|
import { createProjectionMembers } from "./projection-members.js";
|
|
14
|
-
import { getFullyQualifiedSymbolName, getParentTemplateNode, isNeverType, isTemplateInstance, isUnknownType, isVoidType, } from "./type-utils.js";
|
|
19
|
+
import { getFullyQualifiedSymbolName, getParentTemplateNode, isArrayModelType, isErrorType, isNeverType, isNullType, isTemplateInstance, isType, isUnknownType, isValue, isVoidType, } from "./type-utils.js";
|
|
15
20
|
import { IdentifierKind, SyntaxKind, } from "./types.js";
|
|
16
21
|
/**
|
|
17
22
|
* Maps type arguments to type instantiation.
|
|
@@ -105,6 +110,7 @@ export function createChecker(program) {
|
|
|
105
110
|
project,
|
|
106
111
|
neverType,
|
|
107
112
|
errorType,
|
|
113
|
+
nullType,
|
|
108
114
|
anyType: unknownType,
|
|
109
115
|
voidType,
|
|
110
116
|
typePrototype,
|
|
@@ -117,6 +123,8 @@ export function createChecker(program) {
|
|
|
117
123
|
isStdType,
|
|
118
124
|
getStdType,
|
|
119
125
|
resolveTypeReference,
|
|
126
|
+
getValueForNode,
|
|
127
|
+
getTypeOrValueForNode,
|
|
120
128
|
};
|
|
121
129
|
const projectionMembers = createProjectionMembers(checker);
|
|
122
130
|
return checker;
|
|
@@ -207,13 +215,13 @@ export function createChecker(program) {
|
|
|
207
215
|
if (ref.flags & 4096 /* SymbolFlags.Namespace */) {
|
|
208
216
|
const links = getSymbolLinks(getMergedSymbol(ref));
|
|
209
217
|
const type = links.type;
|
|
210
|
-
const decApp =
|
|
218
|
+
const decApp = checkDecoratorApplication(type, decNode, undefined);
|
|
211
219
|
if (decApp) {
|
|
212
220
|
type.decorators.push(decApp);
|
|
213
221
|
applyDecoratorToType(program, decApp, type);
|
|
214
222
|
}
|
|
215
223
|
}
|
|
216
|
-
else if (args.length > 0 || ref.flags &
|
|
224
|
+
else if (args.length > 0 || ref.flags & 67108864 /* SymbolFlags.LateBound */) {
|
|
217
225
|
reportCheckerDiagnostic(createDiagnostic({
|
|
218
226
|
code: "augment-decorator-target",
|
|
219
227
|
messageId: "noInstance",
|
|
@@ -313,9 +321,259 @@ export function createChecker(program) {
|
|
|
313
321
|
return checkOperation(node, mapper, containerType);
|
|
314
322
|
case SyntaxKind.UnionVariant:
|
|
315
323
|
return checkUnionVariant(node, mapper);
|
|
324
|
+
case SyntaxKind.ScalarConstructor:
|
|
325
|
+
return checkScalarConstructor(node, mapper, containerType);
|
|
316
326
|
}
|
|
317
327
|
}
|
|
318
328
|
function getTypeForNode(node, mapper) {
|
|
329
|
+
const entity = checkNode(node, mapper);
|
|
330
|
+
if (entity === null) {
|
|
331
|
+
return errorType;
|
|
332
|
+
}
|
|
333
|
+
if (entity.entityKind === "Indeterminate") {
|
|
334
|
+
return entity.type;
|
|
335
|
+
}
|
|
336
|
+
if (isValue(entity)) {
|
|
337
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
338
|
+
code: "value-in-type",
|
|
339
|
+
target: node,
|
|
340
|
+
}));
|
|
341
|
+
return errorType;
|
|
342
|
+
}
|
|
343
|
+
if (entity.kind === "TemplateParameter") {
|
|
344
|
+
if (entity.constraint?.valueType) {
|
|
345
|
+
// means this template constraint will accept values
|
|
346
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
347
|
+
code: "value-in-type",
|
|
348
|
+
messageId: "referenceTemplate",
|
|
349
|
+
target: node,
|
|
350
|
+
}));
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
return entity;
|
|
354
|
+
}
|
|
355
|
+
function getValueForNode(node, mapper, constraint, options = {}) {
|
|
356
|
+
const initial = checkNode(node, mapper, constraint);
|
|
357
|
+
if (initial === null) {
|
|
358
|
+
return null;
|
|
359
|
+
}
|
|
360
|
+
let entity;
|
|
361
|
+
if (initial.entityKind === "Indeterminate") {
|
|
362
|
+
entity = getValueFromIndeterminate(initial.type, constraint, node);
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
entity = initial;
|
|
366
|
+
}
|
|
367
|
+
if (options.legacyTupleAndModelCast && entity !== null && isType(entity)) {
|
|
368
|
+
entity = legacy_tryTypeToValueCast(entity, constraint, node);
|
|
369
|
+
}
|
|
370
|
+
if (entity === null) {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
if (isValue(entity)) {
|
|
374
|
+
return constraint ? inferScalarsFromConstraints(entity, constraint.type) : entity;
|
|
375
|
+
}
|
|
376
|
+
reportExpectedValue(node, entity);
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
function reportExpectedValue(target, type) {
|
|
380
|
+
if (type.kind === "Model" && type.name === "" && target.kind === SyntaxKind.ModelExpression) {
|
|
381
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
382
|
+
code: "expect-value",
|
|
383
|
+
messageId: "model",
|
|
384
|
+
format: { name: getTypeName(type) },
|
|
385
|
+
codefixes: [createModelToObjectValueCodeFix(target)],
|
|
386
|
+
target,
|
|
387
|
+
}));
|
|
388
|
+
}
|
|
389
|
+
else if (type.kind === "Tuple" && target.kind === SyntaxKind.TupleExpression) {
|
|
390
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
391
|
+
code: "expect-value",
|
|
392
|
+
messageId: "tuple",
|
|
393
|
+
format: { name: getTypeName(type) },
|
|
394
|
+
codefixes: [createTupleToArrayValueCodeFix(target)],
|
|
395
|
+
target,
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
400
|
+
code: "expect-value",
|
|
401
|
+
format: { name: getTypeName(type) },
|
|
402
|
+
target,
|
|
403
|
+
}));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/** In certain context for types that can also be value if the constraint allows it we try to use it as a value instead of a type. */
|
|
407
|
+
function getValueFromIndeterminate(type, constraint, node) {
|
|
408
|
+
switch (type.kind) {
|
|
409
|
+
case "String":
|
|
410
|
+
case "StringTemplate":
|
|
411
|
+
return checkStringValue(type, constraint, node);
|
|
412
|
+
case "Number":
|
|
413
|
+
return checkNumericValue(type, constraint, node);
|
|
414
|
+
case "Boolean":
|
|
415
|
+
return checkBooleanValue(type, constraint, node);
|
|
416
|
+
case "EnumMember":
|
|
417
|
+
return checkEnumValue(type, constraint, node);
|
|
418
|
+
case "UnionVariant":
|
|
419
|
+
return getValueFromIndeterminate(type.type, constraint, node);
|
|
420
|
+
case "Intrinsic":
|
|
421
|
+
switch (type.name) {
|
|
422
|
+
case "null":
|
|
423
|
+
return checkNullValue(type, constraint, node);
|
|
424
|
+
}
|
|
425
|
+
return type;
|
|
426
|
+
default:
|
|
427
|
+
return type;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
function legacy_tryTypeToValueCast(type, constraint, node) {
|
|
431
|
+
switch (type.kind) {
|
|
432
|
+
case "Tuple":
|
|
433
|
+
return legacy_tryUsingTupleAsArrayValue(type, constraint?.type, node);
|
|
434
|
+
case "Model":
|
|
435
|
+
return legacy_tryUsingModelAsObjectValue(type, constraint?.type, node);
|
|
436
|
+
default:
|
|
437
|
+
return type;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// Legacy behavior to smooth transition to object values.
|
|
441
|
+
function legacy_tryUsingModelAsObjectValue(model, type, node) {
|
|
442
|
+
if (model.node?.kind !== SyntaxKind.ModelExpression) {
|
|
443
|
+
return model; // we only want to convert model expressions
|
|
444
|
+
}
|
|
445
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
446
|
+
code: "deprecated",
|
|
447
|
+
codefixes: [createModelToObjectValueCodeFix(model.node)],
|
|
448
|
+
format: {
|
|
449
|
+
message: "Using a model as a value is deprecated. Use an object value instead(with #{}).",
|
|
450
|
+
},
|
|
451
|
+
target: model.node,
|
|
452
|
+
}));
|
|
453
|
+
const value = {
|
|
454
|
+
entityKind: "Value",
|
|
455
|
+
valueKind: "ObjectValue",
|
|
456
|
+
type: type ?? model,
|
|
457
|
+
node: model.node,
|
|
458
|
+
properties: new Map(),
|
|
459
|
+
};
|
|
460
|
+
for (const prop of model.properties.values()) {
|
|
461
|
+
let propValue = getValueFromIndeterminate(prop.type, { kind: "assignment", type: prop.type }, node);
|
|
462
|
+
if (propValue !== null && isType(propValue)) {
|
|
463
|
+
propValue = legacy_tryTypeToValueCast(propValue, { kind: "assignment", type: prop.type }, node);
|
|
464
|
+
}
|
|
465
|
+
if (propValue == null) {
|
|
466
|
+
return null;
|
|
467
|
+
}
|
|
468
|
+
else if (!isValue(propValue)) {
|
|
469
|
+
return model;
|
|
470
|
+
}
|
|
471
|
+
value.properties.set(prop.name, {
|
|
472
|
+
name: prop.name,
|
|
473
|
+
value: propValue,
|
|
474
|
+
node: prop.node,
|
|
475
|
+
});
|
|
476
|
+
}
|
|
477
|
+
if (type !== undefined && !checkTypeAssignable(model, type, node)) {
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
return value;
|
|
481
|
+
}
|
|
482
|
+
// Legacy behavior to smooth transition to array values.
|
|
483
|
+
function legacy_tryUsingTupleAsArrayValue(tuple, type, node) {
|
|
484
|
+
if (tuple.node.kind !== SyntaxKind.TupleExpression) {
|
|
485
|
+
return tuple; // we won't convert dynamic tuples to array values
|
|
486
|
+
}
|
|
487
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
488
|
+
code: "deprecated",
|
|
489
|
+
codefixes: [createTupleToArrayValueCodeFix(tuple.node)],
|
|
490
|
+
format: {
|
|
491
|
+
message: "Using a tuple as a value is deprecated. Use an array value instead(with #[]).",
|
|
492
|
+
},
|
|
493
|
+
target: tuple.node,
|
|
494
|
+
}));
|
|
495
|
+
const values = [];
|
|
496
|
+
for (const [index, item] of tuple.values.entries()) {
|
|
497
|
+
const itemType = type?.kind === "Model" && isArrayModelType(program, type)
|
|
498
|
+
? type.indexer.value
|
|
499
|
+
: type?.kind === "Tuple"
|
|
500
|
+
? type.values[index]
|
|
501
|
+
: undefined;
|
|
502
|
+
let value = getValueFromIndeterminate(item, itemType && { kind: "assignment", type: itemType }, node);
|
|
503
|
+
if (value !== null && isType(value)) {
|
|
504
|
+
value = legacy_tryTypeToValueCast(value, itemType && { kind: "assignment", type: itemType }, node);
|
|
505
|
+
}
|
|
506
|
+
if (value === null) {
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
else if (!isValue(value)) {
|
|
510
|
+
return tuple;
|
|
511
|
+
}
|
|
512
|
+
values.push(value);
|
|
513
|
+
}
|
|
514
|
+
if (type !== undefined && !checkTypeAssignable(tuple, type, node)) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
return {
|
|
518
|
+
entityKind: "Value",
|
|
519
|
+
valueKind: "ArrayValue",
|
|
520
|
+
type: type ?? tuple,
|
|
521
|
+
node: tuple.node,
|
|
522
|
+
values,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Gets a type or value depending on the node and current constraint.
|
|
527
|
+
* For nodes that can be both type or values(e.g. string), the value will be returned if the constraint expect a value of that type even if the constrain also allows the type.
|
|
528
|
+
* This means that if the constraint is `string | valueof string` passing `"abc"` will send the value `"abc"` and not the type `"abc"`.
|
|
529
|
+
*/
|
|
530
|
+
function getTypeOrValueForNode(node, mapper, constraint) {
|
|
531
|
+
const valueConstraint = extractValueOfConstraints(constraint);
|
|
532
|
+
const entity = checkNode(node, mapper, valueConstraint);
|
|
533
|
+
if (entity === null) {
|
|
534
|
+
return entity;
|
|
535
|
+
}
|
|
536
|
+
else if (isType(entity)) {
|
|
537
|
+
if (valueConstraint) {
|
|
538
|
+
return legacy_tryTypeToValueCast(entity, valueConstraint, node);
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
return entity;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
else if (isValue(entity)) {
|
|
545
|
+
return entity;
|
|
546
|
+
}
|
|
547
|
+
compilerAssert(entity.entityKind === "Indeterminate", "Expected indeterminate entity");
|
|
548
|
+
if (valueConstraint) {
|
|
549
|
+
const valueDiagnostics = [];
|
|
550
|
+
const oldDiagnosticHook = onCheckerDiagnostic;
|
|
551
|
+
onCheckerDiagnostic = (x) => valueDiagnostics.push(x);
|
|
552
|
+
const result = getValueFromIndeterminate(entity.type, valueConstraint, node);
|
|
553
|
+
onCheckerDiagnostic = oldDiagnosticHook;
|
|
554
|
+
if (result) {
|
|
555
|
+
// If there were diagnostic reported but we still got a value this means that the value might be invalid.
|
|
556
|
+
reportCheckerDiagnostics(valueDiagnostics);
|
|
557
|
+
return result;
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
return entity.type;
|
|
561
|
+
}
|
|
562
|
+
/** Extact the type constraint a value should match. */
|
|
563
|
+
function extractValueOfConstraints(constraint) {
|
|
564
|
+
if (constraint?.constraint.valueType) {
|
|
565
|
+
return { kind: constraint.kind, type: constraint.constraint.valueType };
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Gets a type, value or indeterminate depending on the node and current constraint.
|
|
573
|
+
* For nodes that can be both type or values(e.g. string literals), an indeterminate entity will be returned.
|
|
574
|
+
* It is the job of of the consumer to decide if it should be a type or a value depending on the context.
|
|
575
|
+
*/
|
|
576
|
+
function checkNode(node, mapper, valueConstraint) {
|
|
319
577
|
switch (node.kind) {
|
|
320
578
|
case SyntaxKind.ModelExpression:
|
|
321
579
|
return checkModel(node, mapper);
|
|
@@ -346,10 +604,10 @@ export function createChecker(program) {
|
|
|
346
604
|
return checkNumericLiteral(node);
|
|
347
605
|
case SyntaxKind.BooleanLiteral:
|
|
348
606
|
return checkBooleanLiteral(node);
|
|
349
|
-
case SyntaxKind.TupleExpression:
|
|
350
|
-
return checkTupleExpression(node, mapper);
|
|
351
607
|
case SyntaxKind.StringLiteral:
|
|
352
608
|
return checkStringLiteral(node);
|
|
609
|
+
case SyntaxKind.TupleExpression:
|
|
610
|
+
return checkTupleExpression(node, mapper);
|
|
353
611
|
case SyntaxKind.StringTemplateExpression:
|
|
354
612
|
return checkStringTemplateExpresion(node, mapper);
|
|
355
613
|
case SyntaxKind.ArrayExpression:
|
|
@@ -363,7 +621,7 @@ export function createChecker(program) {
|
|
|
363
621
|
case SyntaxKind.FunctionDeclarationStatement:
|
|
364
622
|
return checkFunctionDeclaration(node, mapper);
|
|
365
623
|
case SyntaxKind.TypeReference:
|
|
366
|
-
return
|
|
624
|
+
return checkTypeOrValueReference(node, mapper);
|
|
367
625
|
case SyntaxKind.TemplateArgument:
|
|
368
626
|
return checkTemplateArgument(node, mapper);
|
|
369
627
|
case SyntaxKind.TemplateParameterDeclaration:
|
|
@@ -376,12 +634,19 @@ export function createChecker(program) {
|
|
|
376
634
|
return neverType;
|
|
377
635
|
case SyntaxKind.UnknownKeyword:
|
|
378
636
|
return unknownType;
|
|
637
|
+
case SyntaxKind.ObjectLiteral:
|
|
638
|
+
return checkObjectValue(node, mapper, valueConstraint);
|
|
639
|
+
case SyntaxKind.ArrayLiteral:
|
|
640
|
+
return checkArrayValue(node, mapper, valueConstraint);
|
|
641
|
+
case SyntaxKind.ConstStatement:
|
|
642
|
+
return checkConst(node);
|
|
643
|
+
case SyntaxKind.CallExpression:
|
|
644
|
+
return checkCallExpression(node, mapper);
|
|
645
|
+
case SyntaxKind.TypeOfExpression:
|
|
646
|
+
return checkTypeOfExpression(node, mapper);
|
|
647
|
+
default:
|
|
648
|
+
return errorType;
|
|
379
649
|
}
|
|
380
|
-
// we don't emit an error here as we blindly call this function
|
|
381
|
-
// with any node type, but some nodes don't produce a type
|
|
382
|
-
// (e.g. imports). errorType should result in an error if it
|
|
383
|
-
// bubbles out somewhere its not supposed to be.
|
|
384
|
-
return errorType;
|
|
385
650
|
}
|
|
386
651
|
/**
|
|
387
652
|
* Return a fully qualified id of node
|
|
@@ -440,7 +705,7 @@ export function createChecker(program) {
|
|
|
440
705
|
});
|
|
441
706
|
if (node.constraint) {
|
|
442
707
|
pendingResolutions.start(getNodeSymId(node), ResolutionKind.Constraint);
|
|
443
|
-
type.constraint =
|
|
708
|
+
type.constraint = getParamConstraintEntityForNode(node.constraint);
|
|
444
709
|
pendingResolutions.finish(getNodeSymId(node), ResolutionKind.Constraint);
|
|
445
710
|
}
|
|
446
711
|
if (node.default) {
|
|
@@ -453,23 +718,24 @@ export function createChecker(program) {
|
|
|
453
718
|
if (declaredType.default === undefined) {
|
|
454
719
|
return undefined;
|
|
455
720
|
}
|
|
456
|
-
if (isErrorType(declaredType.default))
|
|
721
|
+
if ((isType(declaredType.default) && isErrorType(declaredType.default)) ||
|
|
722
|
+
declaredType.default === null) {
|
|
457
723
|
return declaredType.default;
|
|
458
724
|
}
|
|
459
|
-
return
|
|
725
|
+
return checkNode(node.default, mapper);
|
|
460
726
|
}
|
|
461
727
|
function checkTemplateParameterDefault(nodeDefault, templateParameters, index, constraint) {
|
|
462
728
|
function visit(node) {
|
|
463
|
-
const
|
|
729
|
+
const entity = checkNode(node);
|
|
464
730
|
let hasError = false;
|
|
465
|
-
if (
|
|
731
|
+
if (entity !== null && "kind" in entity && entity.kind === "TemplateParameter") {
|
|
466
732
|
for (let i = index; i < templateParameters.length; i++) {
|
|
467
|
-
if (
|
|
733
|
+
if (entity.node.symbol === templateParameters[i].symbol) {
|
|
468
734
|
reportCheckerDiagnostic(createDiagnostic({ code: "invalid-template-default", target: node }));
|
|
469
735
|
return undefined;
|
|
470
736
|
}
|
|
471
737
|
}
|
|
472
|
-
return
|
|
738
|
+
return entity;
|
|
473
739
|
}
|
|
474
740
|
visitChildren(node, (x) => {
|
|
475
741
|
const visited = visit(x);
|
|
@@ -477,10 +743,10 @@ export function createChecker(program) {
|
|
|
477
743
|
hasError = true;
|
|
478
744
|
}
|
|
479
745
|
});
|
|
480
|
-
return hasError ? undefined :
|
|
746
|
+
return hasError ? undefined : entity;
|
|
481
747
|
}
|
|
482
748
|
const type = visit(nodeDefault) ?? errorType;
|
|
483
|
-
if (!isErrorType(type) && constraint) {
|
|
749
|
+
if (!("kind" in type && isErrorType(type)) && constraint) {
|
|
484
750
|
checkTypeAssignable(type, constraint, nodeDefault);
|
|
485
751
|
}
|
|
486
752
|
return type;
|
|
@@ -500,8 +766,22 @@ export function createChecker(program) {
|
|
|
500
766
|
const type = checkTypeReferenceSymbol(sym, node, mapper, instantiateTemplate);
|
|
501
767
|
return type;
|
|
502
768
|
}
|
|
769
|
+
/**
|
|
770
|
+
* Check and resolve a type for the given type reference node.
|
|
771
|
+
* @param node Node.
|
|
772
|
+
* @param mapper Type mapper for template instantiation context.
|
|
773
|
+
* @param instantiateTemplate If templated type should be instantiated if they haven't yet.
|
|
774
|
+
* @returns Resolved type.
|
|
775
|
+
*/
|
|
776
|
+
function checkTypeOrValueReference(node, mapper, instantiateTemplate = true) {
|
|
777
|
+
const sym = resolveTypeReferenceSym(node, mapper);
|
|
778
|
+
if (!sym) {
|
|
779
|
+
return errorType;
|
|
780
|
+
}
|
|
781
|
+
return checkTypeOrValueReferenceSymbol(sym, node, mapper, instantiateTemplate) ?? errorType;
|
|
782
|
+
}
|
|
503
783
|
function checkTemplateArgument(node, mapper) {
|
|
504
|
-
return
|
|
784
|
+
return checkNode(node.argument, mapper);
|
|
505
785
|
}
|
|
506
786
|
function resolveTypeReference(node) {
|
|
507
787
|
const oldDiagnosticHook = onCheckerDiagnostic;
|
|
@@ -567,8 +847,8 @@ export function createChecker(program) {
|
|
|
567
847
|
function checkTemplateInstantiationArgs(node, args, decls, mapper) {
|
|
568
848
|
const params = new Map();
|
|
569
849
|
const positional = [];
|
|
570
|
-
const initMap = new Map(decls.map(
|
|
571
|
-
const declaredType =
|
|
850
|
+
const initMap = new Map(decls.map((decl) => {
|
|
851
|
+
const declaredType = checkTemplateParameterDeclaration(decl, undefined);
|
|
572
852
|
positional.push(declaredType);
|
|
573
853
|
params.set(decl.id.sv, declaredType);
|
|
574
854
|
return [
|
|
@@ -582,7 +862,7 @@ export function createChecker(program) {
|
|
|
582
862
|
let named = false;
|
|
583
863
|
for (const [arg, idx] of args.map((v, i) => [v, i])) {
|
|
584
864
|
function deferredCheck() {
|
|
585
|
-
return [arg,
|
|
865
|
+
return [arg, checkNode(arg.argument, mapper)];
|
|
586
866
|
}
|
|
587
867
|
if (arg.name) {
|
|
588
868
|
named = true;
|
|
@@ -657,18 +937,30 @@ export function createChecker(program) {
|
|
|
657
937
|
target: node,
|
|
658
938
|
}));
|
|
659
939
|
// TODO-TIM check if we expose this below
|
|
660
|
-
commit(param, param.constraint?.
|
|
940
|
+
commit(param, param.constraint?.type ?? unknownType);
|
|
661
941
|
}
|
|
662
942
|
continue;
|
|
663
943
|
}
|
|
664
944
|
const [argNode, type] = init();
|
|
945
|
+
if (type === null) {
|
|
946
|
+
commit(param, unknownType);
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
665
949
|
if (param.constraint) {
|
|
666
|
-
const constraint = param.constraint.kind === "TemplateParameter"
|
|
667
|
-
? finalMap.get(param.constraint)
|
|
950
|
+
const constraint = param.constraint.type?.kind === "TemplateParameter"
|
|
951
|
+
? finalMap.get(param.constraint.type)
|
|
668
952
|
: param.constraint;
|
|
669
|
-
if (
|
|
670
|
-
|
|
671
|
-
|
|
953
|
+
if (isType(type) && param.constraint?.valueType) {
|
|
954
|
+
const converted = legacy_tryTypeToValueCast(type, { kind: "argument", type: param.constraint.valueType }, argNode);
|
|
955
|
+
// If we manage to convert it means this might be convertable so we skip type checking.
|
|
956
|
+
// However we still return the original entity
|
|
957
|
+
if (converted !== type) {
|
|
958
|
+
commit(param, type);
|
|
959
|
+
continue;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
if (param.constraint && !checkArgumentAssignable(type, constraint, argNode)) {
|
|
963
|
+
const effectiveType = param.constraint.type ?? unknownType;
|
|
672
964
|
commit(param, effectiveType);
|
|
673
965
|
continue;
|
|
674
966
|
}
|
|
@@ -679,6 +971,15 @@ export function createChecker(program) {
|
|
|
679
971
|
commit(param, unknownType);
|
|
680
972
|
continue;
|
|
681
973
|
}
|
|
974
|
+
else if (isValue(type)) {
|
|
975
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
976
|
+
code: "value-in-type",
|
|
977
|
+
messageId: "noTemplateConstraint",
|
|
978
|
+
target: argNode,
|
|
979
|
+
}));
|
|
980
|
+
commit(param, unknownType);
|
|
981
|
+
continue;
|
|
982
|
+
}
|
|
682
983
|
commit(param, type);
|
|
683
984
|
}
|
|
684
985
|
return finalMap;
|
|
@@ -692,6 +993,20 @@ export function createChecker(program) {
|
|
|
692
993
|
* @returns resolved type.
|
|
693
994
|
*/
|
|
694
995
|
function checkTypeReferenceSymbol(sym, node, mapper, instantiateTemplates = true) {
|
|
996
|
+
const result = checkTypeOrValueReferenceSymbol(sym, node, mapper, instantiateTemplates);
|
|
997
|
+
if (result === null || isValue(result)) {
|
|
998
|
+
reportCheckerDiagnostic(createDiagnostic({ code: "value-in-type", target: node }));
|
|
999
|
+
return errorType;
|
|
1000
|
+
}
|
|
1001
|
+
if (result.entityKind === "Indeterminate") {
|
|
1002
|
+
return result.type;
|
|
1003
|
+
}
|
|
1004
|
+
return result;
|
|
1005
|
+
}
|
|
1006
|
+
function checkTypeOrValueReferenceSymbol(sym, node, mapper, instantiateTemplates = true) {
|
|
1007
|
+
if (sym.flags & 16777216 /* SymbolFlags.Const */) {
|
|
1008
|
+
return getValueForNode(sym.declarations[0], mapper);
|
|
1009
|
+
}
|
|
695
1010
|
if (sym.flags & 16384 /* SymbolFlags.Decorator */) {
|
|
696
1011
|
reportCheckerDiagnostic(createDiagnostic({ code: "invalid-type-ref", messageId: "decorator", target: sym }));
|
|
697
1012
|
return errorType;
|
|
@@ -719,14 +1034,14 @@ export function createChecker(program) {
|
|
|
719
1034
|
target: node,
|
|
720
1035
|
}));
|
|
721
1036
|
}
|
|
722
|
-
if (sym.flags &
|
|
1037
|
+
if (sym.flags & 67108864 /* SymbolFlags.LateBound */) {
|
|
723
1038
|
compilerAssert(sym.type, "Expected late bound symbol to have type");
|
|
724
1039
|
return sym.type;
|
|
725
1040
|
}
|
|
726
1041
|
else if (symbolLinks.declaredType) {
|
|
727
1042
|
baseType = symbolLinks.declaredType;
|
|
728
1043
|
}
|
|
729
|
-
else if (sym.flags &
|
|
1044
|
+
else if (sym.flags & 33555780 /* SymbolFlags.Member */) {
|
|
730
1045
|
baseType = checkMemberSym(sym, mapper);
|
|
731
1046
|
}
|
|
732
1047
|
else {
|
|
@@ -749,12 +1064,13 @@ export function createChecker(program) {
|
|
|
749
1064
|
target: node,
|
|
750
1065
|
}));
|
|
751
1066
|
}
|
|
752
|
-
if (sym.flags &
|
|
753
|
-
compilerAssert(sym.type,
|
|
1067
|
+
if (sym.flags & 67108864 /* SymbolFlags.LateBound */) {
|
|
1068
|
+
compilerAssert(sym.type, `Expected late bound symbol to have type`);
|
|
754
1069
|
return sym.type;
|
|
755
1070
|
}
|
|
756
1071
|
else if (sym.flags & 32768 /* SymbolFlags.TemplateParameter */) {
|
|
757
|
-
|
|
1072
|
+
const mapped = checkTemplateParameterDeclaration(sym.declarations[0], mapper);
|
|
1073
|
+
baseType = mapped;
|
|
758
1074
|
}
|
|
759
1075
|
else if (symbolLinks.type) {
|
|
760
1076
|
// Have a cached type for non-declarations
|
|
@@ -764,7 +1080,7 @@ export function createChecker(program) {
|
|
|
764
1080
|
baseType = symbolLinks.declaredType;
|
|
765
1081
|
}
|
|
766
1082
|
else {
|
|
767
|
-
if (sym.flags &
|
|
1083
|
+
if (sym.flags & 33555780 /* SymbolFlags.Member */) {
|
|
768
1084
|
baseType = checkMemberSym(sym, mapper);
|
|
769
1085
|
}
|
|
770
1086
|
else {
|
|
@@ -783,6 +1099,12 @@ export function createChecker(program) {
|
|
|
783
1099
|
checkDeprecated(baseType, declarationNode, node);
|
|
784
1100
|
}
|
|
785
1101
|
}
|
|
1102
|
+
// Elements that could be used as type or values depending on the context
|
|
1103
|
+
if (baseType.kind === "EnumMember" ||
|
|
1104
|
+
baseType.kind === "UnionVariant" ||
|
|
1105
|
+
isNullType(baseType)) {
|
|
1106
|
+
return createIndeterminateEntity(baseType);
|
|
1107
|
+
}
|
|
786
1108
|
return baseType;
|
|
787
1109
|
}
|
|
788
1110
|
/**
|
|
@@ -797,11 +1119,11 @@ export function createChecker(program) {
|
|
|
797
1119
|
if (symbolLinks.declaredType) {
|
|
798
1120
|
return symbolLinks.declaredType;
|
|
799
1121
|
}
|
|
800
|
-
if (sym.flags &
|
|
1122
|
+
if (sym.flags & 67108864 /* SymbolFlags.LateBound */) {
|
|
801
1123
|
compilerAssert(sym.type, "Expected late bound symbol to have type");
|
|
802
1124
|
return sym.type;
|
|
803
1125
|
}
|
|
804
|
-
if (sym.flags &
|
|
1126
|
+
if (sym.flags & 33555780 /* SymbolFlags.Member */) {
|
|
805
1127
|
return checkMemberSym(sym, mapper);
|
|
806
1128
|
}
|
|
807
1129
|
else {
|
|
@@ -816,7 +1138,7 @@ export function createChecker(program) {
|
|
|
816
1138
|
* @returns The declared type for the given node.
|
|
817
1139
|
*/
|
|
818
1140
|
function checkDeclaredType(sym, node, mapper) {
|
|
819
|
-
|
|
1141
|
+
const type = sym.flags & 2 /* SymbolFlags.Model */
|
|
820
1142
|
? checkModelStatement(node, mapper)
|
|
821
1143
|
: sym.flags & 8 /* SymbolFlags.Scalar */
|
|
822
1144
|
? checkScalar(node, mapper)
|
|
@@ -827,6 +1149,7 @@ export function createChecker(program) {
|
|
|
827
1149
|
: sym.flags & 16 /* SymbolFlags.Operation */
|
|
828
1150
|
? checkOperation(node, mapper)
|
|
829
1151
|
: checkUnion(node, mapper);
|
|
1152
|
+
return type;
|
|
830
1153
|
}
|
|
831
1154
|
function getOrInstantiateTemplate(templateNode, params, args, parentMapper, instantiateTempalates = true) {
|
|
832
1155
|
const symbolLinks = templateNode.kind === SyntaxKind.OperationStatement &&
|
|
@@ -874,6 +1197,57 @@ export function createChecker(program) {
|
|
|
874
1197
|
}
|
|
875
1198
|
return type;
|
|
876
1199
|
}
|
|
1200
|
+
/** Check a union expresion used in a parameter constraint, those allow the use of `valueof` as a variant. */
|
|
1201
|
+
function checkMixedParameterConstraintUnion(node, mapper) {
|
|
1202
|
+
const values = [];
|
|
1203
|
+
const types = [];
|
|
1204
|
+
for (const option of node.options) {
|
|
1205
|
+
const [kind, type] = getTypeOrValueOfTypeForNode(option, mapper);
|
|
1206
|
+
if (kind === "value") {
|
|
1207
|
+
values.push(type);
|
|
1208
|
+
}
|
|
1209
|
+
else {
|
|
1210
|
+
types.push(type);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
entityKind: "MixedParameterConstraint",
|
|
1215
|
+
node,
|
|
1216
|
+
valueType: values.length === 0
|
|
1217
|
+
? undefined
|
|
1218
|
+
: values.length === 1
|
|
1219
|
+
? values[0]
|
|
1220
|
+
: createConstraintUnion(node, values),
|
|
1221
|
+
type: types.length === 0
|
|
1222
|
+
? undefined
|
|
1223
|
+
: types.length === 1
|
|
1224
|
+
? types[0]
|
|
1225
|
+
: createConstraintUnion(node, types),
|
|
1226
|
+
};
|
|
1227
|
+
}
|
|
1228
|
+
function createConstraintUnion(node, options) {
|
|
1229
|
+
const variants = createRekeyableMap();
|
|
1230
|
+
const union = createAndFinishType({
|
|
1231
|
+
kind: "Union",
|
|
1232
|
+
node,
|
|
1233
|
+
options,
|
|
1234
|
+
decorators: [],
|
|
1235
|
+
variants,
|
|
1236
|
+
expression: true,
|
|
1237
|
+
});
|
|
1238
|
+
for (const option of options) {
|
|
1239
|
+
const name = Symbol("indexer-union-variant");
|
|
1240
|
+
variants.set(name, createAndFinishType({
|
|
1241
|
+
kind: "UnionVariant",
|
|
1242
|
+
node: undefined,
|
|
1243
|
+
type: option,
|
|
1244
|
+
name,
|
|
1245
|
+
union,
|
|
1246
|
+
decorators: [],
|
|
1247
|
+
}));
|
|
1248
|
+
}
|
|
1249
|
+
return union;
|
|
1250
|
+
}
|
|
877
1251
|
function checkUnionExpression(node, mapper) {
|
|
878
1252
|
const unionType = createAndFinishType({
|
|
879
1253
|
kind: "Union",
|
|
@@ -910,13 +1284,6 @@ export function createChecker(program) {
|
|
|
910
1284
|
}
|
|
911
1285
|
return unionType;
|
|
912
1286
|
}
|
|
913
|
-
function checkValueOfExpression(node, mapper) {
|
|
914
|
-
const target = getTypeForNode(node.target, mapper);
|
|
915
|
-
return {
|
|
916
|
-
kind: "Value",
|
|
917
|
-
target,
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
1287
|
/**
|
|
921
1288
|
* Intersection produces a model type from the properties of its operands.
|
|
922
1289
|
* So this doesn't work if we don't have a known set of properties (e.g.
|
|
@@ -948,14 +1315,43 @@ export function createChecker(program) {
|
|
|
948
1315
|
name: `@${name}`,
|
|
949
1316
|
namespace,
|
|
950
1317
|
node,
|
|
951
|
-
target: checkFunctionParameter(node.target, mapper),
|
|
952
|
-
parameters: node.parameters.map((x) => checkFunctionParameter(x, mapper)),
|
|
1318
|
+
target: checkFunctionParameter(node.target, mapper, true),
|
|
1319
|
+
parameters: node.parameters.map((x) => checkFunctionParameter(x, mapper, true)),
|
|
953
1320
|
implementation: implementation ?? (() => { }),
|
|
954
1321
|
});
|
|
955
1322
|
namespace.decoratorDeclarations.set(name, decoratorType);
|
|
956
1323
|
linkType(links, decoratorType, mapper);
|
|
1324
|
+
checkDecoratorLegacyMarshalling(decoratorType);
|
|
957
1325
|
return decoratorType;
|
|
958
1326
|
}
|
|
1327
|
+
function checkDecoratorLegacyMarshalling(decorator) {
|
|
1328
|
+
const marshalling = resolveDecoratorArgMarshalling(decorator);
|
|
1329
|
+
function reportDeprecatedLegacyMarshalling(param, message) {
|
|
1330
|
+
reportDeprecated(program, [
|
|
1331
|
+
`Parameter ${param.name} of decorator ${decorator.name} is using legacy marshalling but is accepting ${message}.`,
|
|
1332
|
+
`This will change in the future.`,
|
|
1333
|
+
'Add `export const $flags = {decoratorArgMarshalling: "new"}}` to your library to opt-in to the new marshalling behavior.',
|
|
1334
|
+
].join("\n"), param.node);
|
|
1335
|
+
}
|
|
1336
|
+
if (marshalling === "legacy") {
|
|
1337
|
+
for (const param of decorator.parameters) {
|
|
1338
|
+
if (param.type.valueType) {
|
|
1339
|
+
if (ignoreDiagnostics(isTypeAssignableTo(nullType, param.type.valueType, param.type))) {
|
|
1340
|
+
reportDeprecatedLegacyMarshalling(param, "null as a type");
|
|
1341
|
+
}
|
|
1342
|
+
else if (param.type.valueType.kind === "Enum" ||
|
|
1343
|
+
param.type.valueType.kind === "EnumMember" ||
|
|
1344
|
+
(isReflectionType(param.type.valueType) && param.type.valueType.name === "EnumMember")) {
|
|
1345
|
+
reportDeprecatedLegacyMarshalling(param, "enum members");
|
|
1346
|
+
}
|
|
1347
|
+
else if (ignoreDiagnostics(isTypeAssignableTo(param.type.valueType, getStdType("numeric"), param.type.valueType)) &&
|
|
1348
|
+
!canNumericConstraintBeJsNumber(param.type.valueType)) {
|
|
1349
|
+
reportDeprecatedLegacyMarshalling(param, "a numeric type that is not representable as a JS Number");
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
959
1355
|
function checkFunctionDeclaration(node, mapper) {
|
|
960
1356
|
const symbol = getMergedSymbol(node.symbol);
|
|
961
1357
|
const links = getSymbolLinks(symbol);
|
|
@@ -978,7 +1374,7 @@ export function createChecker(program) {
|
|
|
978
1374
|
name,
|
|
979
1375
|
namespace,
|
|
980
1376
|
node,
|
|
981
|
-
parameters: node.parameters.map((x) => checkFunctionParameter(x, mapper)),
|
|
1377
|
+
parameters: node.parameters.map((x) => checkFunctionParameter(x, mapper, true)),
|
|
982
1378
|
returnType: node.returnType ? getTypeForNode(node.returnType, mapper) : unknownType,
|
|
983
1379
|
implementation: implementation ?? (() => { }),
|
|
984
1380
|
});
|
|
@@ -986,7 +1382,7 @@ export function createChecker(program) {
|
|
|
986
1382
|
linkType(links, functionType, mapper);
|
|
987
1383
|
return functionType;
|
|
988
1384
|
}
|
|
989
|
-
function checkFunctionParameter(node, mapper) {
|
|
1385
|
+
function checkFunctionParameter(node, mapper, mixed) {
|
|
990
1386
|
const links = getSymbolLinks(node.symbol);
|
|
991
1387
|
if (links.declaredType) {
|
|
992
1388
|
return links.declaredType;
|
|
@@ -998,24 +1394,62 @@ export function createChecker(program) {
|
|
|
998
1394
|
node.type.target.kind === SyntaxKind.ArrayExpression))) {
|
|
999
1395
|
reportCheckerDiagnostic(createDiagnostic({ code: "rest-parameter-array", target: node.type }));
|
|
1000
1396
|
}
|
|
1001
|
-
const
|
|
1002
|
-
const parameterType = createType({
|
|
1397
|
+
const base = {
|
|
1003
1398
|
kind: "FunctionParameter",
|
|
1004
1399
|
node,
|
|
1005
1400
|
name: node.id.sv,
|
|
1006
1401
|
optional: node.optional,
|
|
1007
1402
|
rest: node.rest,
|
|
1008
|
-
type,
|
|
1009
1403
|
implementation: node.symbol.value,
|
|
1010
|
-
}
|
|
1404
|
+
};
|
|
1405
|
+
let parameterType;
|
|
1406
|
+
if (mixed) {
|
|
1407
|
+
const type = node.type
|
|
1408
|
+
? getParamConstraintEntityForNode(node.type)
|
|
1409
|
+
: ({
|
|
1410
|
+
entityKind: "MixedParameterConstraint",
|
|
1411
|
+
type: unknownType,
|
|
1412
|
+
});
|
|
1413
|
+
parameterType = createType({
|
|
1414
|
+
...base,
|
|
1415
|
+
type,
|
|
1416
|
+
mixed: true,
|
|
1417
|
+
implementation: node.symbol.value,
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
else {
|
|
1421
|
+
parameterType = createType({
|
|
1422
|
+
...base,
|
|
1423
|
+
mixed: false,
|
|
1424
|
+
type: node.type ? getTypeForNode(node.type) : unknownType,
|
|
1425
|
+
implementation: node.symbol.value,
|
|
1426
|
+
});
|
|
1427
|
+
}
|
|
1011
1428
|
linkType(links, parameterType, mapper);
|
|
1012
1429
|
return parameterType;
|
|
1013
1430
|
}
|
|
1014
|
-
function
|
|
1015
|
-
|
|
1016
|
-
|
|
1431
|
+
function getTypeOrValueOfTypeForNode(node, mapper) {
|
|
1432
|
+
switch (node.kind) {
|
|
1433
|
+
case SyntaxKind.ValueOfExpression:
|
|
1434
|
+
const target = getTypeForNode(node.target, mapper);
|
|
1435
|
+
return ["value", target];
|
|
1436
|
+
default:
|
|
1437
|
+
return ["type", getTypeForNode(node, mapper)];
|
|
1438
|
+
}
|
|
1439
|
+
}
|
|
1440
|
+
function getParamConstraintEntityForNode(node, mapper) {
|
|
1441
|
+
switch (node.kind) {
|
|
1442
|
+
case SyntaxKind.UnionExpression:
|
|
1443
|
+
return checkMixedParameterConstraintUnion(node, mapper);
|
|
1444
|
+
default:
|
|
1445
|
+
const [kind, entity] = getTypeOrValueOfTypeForNode(node, mapper);
|
|
1446
|
+
return {
|
|
1447
|
+
entityKind: "MixedParameterConstraint",
|
|
1448
|
+
node: node,
|
|
1449
|
+
type: kind === "value" ? undefined : entity,
|
|
1450
|
+
valueType: kind === "value" ? entity : undefined,
|
|
1451
|
+
};
|
|
1017
1452
|
}
|
|
1018
|
-
return getTypeForNode(node, mapper);
|
|
1019
1453
|
}
|
|
1020
1454
|
function mergeModelTypes(node, options, mapper) {
|
|
1021
1455
|
const properties = createRekeyableMap();
|
|
@@ -1104,7 +1538,7 @@ export function createChecker(program) {
|
|
|
1104
1538
|
}
|
|
1105
1539
|
if (node.kind === SyntaxKind.NamespaceStatement) {
|
|
1106
1540
|
if (isArray(node.statements)) {
|
|
1107
|
-
node.statements.forEach((x) =>
|
|
1541
|
+
node.statements.forEach((x) => checkNode(x));
|
|
1108
1542
|
}
|
|
1109
1543
|
else if (node.statements) {
|
|
1110
1544
|
const subNs = checkNamespace(node.statements);
|
|
@@ -1397,7 +1831,10 @@ export function createChecker(program) {
|
|
|
1397
1831
|
break;
|
|
1398
1832
|
}
|
|
1399
1833
|
compilerAssert(node.parent, "Parent expected.");
|
|
1400
|
-
const containerType =
|
|
1834
|
+
const containerType = getTypeOrValueForNode(node.parent, mapper);
|
|
1835
|
+
if (containerType === null || isValue(containerType)) {
|
|
1836
|
+
return undefined;
|
|
1837
|
+
}
|
|
1401
1838
|
if (isAnonymous(containerType)) {
|
|
1402
1839
|
return undefined; // member of anonymous type cannot be referenced.
|
|
1403
1840
|
}
|
|
@@ -1746,9 +2183,9 @@ export function createChecker(program) {
|
|
|
1746
2183
|
}));
|
|
1747
2184
|
return undefined;
|
|
1748
2185
|
}
|
|
1749
|
-
else if (base.flags &
|
|
2186
|
+
else if (base.flags & 682 /* SymbolFlags.MemberContainer */) {
|
|
1750
2187
|
if (options.checkTemplateTypes && isTemplatedNode(base.declarations[0])) {
|
|
1751
|
-
const type = base.flags &
|
|
2188
|
+
const type = base.flags & 67108864 /* SymbolFlags.LateBound */
|
|
1752
2189
|
? base.type
|
|
1753
2190
|
: getTypeForNode(base.declarations[0], mapper);
|
|
1754
2191
|
if (isTemplateInstance(type)) {
|
|
@@ -1863,17 +2300,112 @@ export function createChecker(program) {
|
|
|
1863
2300
|
}
|
|
1864
2301
|
}
|
|
1865
2302
|
function checkStringTemplateExpresion(node, mapper) {
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
2303
|
+
let hasType = false;
|
|
2304
|
+
let hasValue = false;
|
|
2305
|
+
const spanTypeOrValues = node.spans.map((span) => [span, checkNode(span.expression, mapper)]);
|
|
2306
|
+
for (const [_, typeOrValue] of spanTypeOrValues) {
|
|
2307
|
+
if (typeOrValue !== null) {
|
|
2308
|
+
if (isValue(typeOrValue)) {
|
|
2309
|
+
hasValue = true;
|
|
2310
|
+
}
|
|
2311
|
+
else if ("kind" in typeOrValue && typeOrValue.kind === "TemplateParameter") {
|
|
2312
|
+
if (typeOrValue.constraint) {
|
|
2313
|
+
if (typeOrValue.constraint.valueType) {
|
|
2314
|
+
hasValue = true;
|
|
2315
|
+
}
|
|
2316
|
+
if (typeOrValue.constraint.type) {
|
|
2317
|
+
hasType = true;
|
|
2318
|
+
}
|
|
2319
|
+
}
|
|
2320
|
+
else {
|
|
2321
|
+
hasType = true;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
else {
|
|
2325
|
+
hasType = true;
|
|
2326
|
+
}
|
|
2327
|
+
}
|
|
2328
|
+
}
|
|
2329
|
+
if (hasType && hasValue) {
|
|
2330
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2331
|
+
code: "mixed-string-template",
|
|
2332
|
+
target: node,
|
|
2333
|
+
}));
|
|
2334
|
+
return null;
|
|
2335
|
+
}
|
|
2336
|
+
if (hasValue) {
|
|
2337
|
+
let str = node.head.value;
|
|
2338
|
+
for (const [span, typeOrValue] of spanTypeOrValues) {
|
|
2339
|
+
if (typeOrValue !== null &&
|
|
2340
|
+
(!("kind" in typeOrValue) || typeOrValue.kind !== "TemplateParameter")) {
|
|
2341
|
+
compilerAssert(typeOrValue !== null && isValue(typeOrValue), "Expected value.");
|
|
2342
|
+
str += stringifyValueForTemplate(typeOrValue);
|
|
2343
|
+
}
|
|
2344
|
+
str += span.literal.value;
|
|
2345
|
+
}
|
|
2346
|
+
return checkStringValue(createLiteralType(str), undefined, node);
|
|
2347
|
+
}
|
|
2348
|
+
else {
|
|
2349
|
+
let hasNonStringElement = false;
|
|
2350
|
+
let stringValue = node.head.value;
|
|
2351
|
+
const spans = [createTemplateSpanLiteral(node.head)];
|
|
2352
|
+
for (const [span, typeOrValue] of spanTypeOrValues) {
|
|
2353
|
+
compilerAssert(typeOrValue !== null && !isValue(typeOrValue), "Expected type.");
|
|
2354
|
+
const type = typeOrValue.entityKind === "Indeterminate" ? typeOrValue.type : typeOrValue;
|
|
2355
|
+
const spanValue = createTemplateSpanValue(span.expression, type);
|
|
2356
|
+
spans.push(spanValue);
|
|
2357
|
+
const spanValueAsString = stringifyTypeForTemplate(type);
|
|
2358
|
+
if (spanValueAsString) {
|
|
2359
|
+
stringValue += spanValueAsString;
|
|
2360
|
+
}
|
|
2361
|
+
else {
|
|
2362
|
+
hasNonStringElement = true;
|
|
2363
|
+
}
|
|
2364
|
+
spans.push(createTemplateSpanLiteral(span.literal));
|
|
2365
|
+
stringValue += span.literal.value;
|
|
2366
|
+
}
|
|
2367
|
+
return createIndeterminateEntity(createType({
|
|
2368
|
+
kind: "StringTemplate",
|
|
2369
|
+
node,
|
|
2370
|
+
spans,
|
|
2371
|
+
stringValue: hasNonStringElement ? undefined : stringValue,
|
|
2372
|
+
}));
|
|
2373
|
+
}
|
|
2374
|
+
}
|
|
2375
|
+
function createIndeterminateEntity(type) {
|
|
2376
|
+
return {
|
|
2377
|
+
entityKind: "Indeterminate",
|
|
2378
|
+
type,
|
|
2379
|
+
};
|
|
2380
|
+
}
|
|
2381
|
+
function stringifyTypeForTemplate(type) {
|
|
2382
|
+
switch (type.kind) {
|
|
2383
|
+
case "String":
|
|
2384
|
+
case "Number":
|
|
2385
|
+
case "Boolean":
|
|
2386
|
+
return String(type.value);
|
|
2387
|
+
case "StringTemplate":
|
|
2388
|
+
if (type.stringValue !== undefined) {
|
|
2389
|
+
return type.stringValue;
|
|
2390
|
+
}
|
|
2391
|
+
return undefined;
|
|
2392
|
+
default:
|
|
2393
|
+
return undefined;
|
|
2394
|
+
}
|
|
2395
|
+
}
|
|
2396
|
+
function stringifyValueForTemplate(value) {
|
|
2397
|
+
switch (value.valueKind) {
|
|
2398
|
+
case "StringValue":
|
|
2399
|
+
case "NumericValue":
|
|
2400
|
+
case "BooleanValue":
|
|
2401
|
+
return value.value.toString();
|
|
2402
|
+
default:
|
|
2403
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2404
|
+
code: "non-literal-string-template",
|
|
2405
|
+
target: value,
|
|
2406
|
+
}));
|
|
2407
|
+
return `[${value.valueKind}]`;
|
|
1870
2408
|
}
|
|
1871
|
-
const type = createType({
|
|
1872
|
-
kind: "StringTemplate",
|
|
1873
|
-
node,
|
|
1874
|
-
spans,
|
|
1875
|
-
});
|
|
1876
|
-
return type;
|
|
1877
2409
|
}
|
|
1878
2410
|
function createTemplateSpanLiteral(node) {
|
|
1879
2411
|
return createType({
|
|
@@ -1883,22 +2415,31 @@ export function createChecker(program) {
|
|
|
1883
2415
|
type: getLiteralType(node),
|
|
1884
2416
|
});
|
|
1885
2417
|
}
|
|
1886
|
-
function createTemplateSpanValue(node,
|
|
2418
|
+
function createTemplateSpanValue(node, type) {
|
|
1887
2419
|
return createType({
|
|
1888
2420
|
kind: "StringTemplateSpan",
|
|
1889
2421
|
node: node,
|
|
1890
2422
|
isInterpolated: true,
|
|
1891
|
-
type:
|
|
2423
|
+
type: type,
|
|
1892
2424
|
});
|
|
1893
2425
|
}
|
|
1894
2426
|
function checkStringLiteral(str) {
|
|
1895
|
-
return
|
|
2427
|
+
return {
|
|
2428
|
+
entityKind: "Indeterminate",
|
|
2429
|
+
type: getLiteralType(str),
|
|
2430
|
+
};
|
|
1896
2431
|
}
|
|
1897
2432
|
function checkNumericLiteral(num) {
|
|
1898
|
-
return
|
|
2433
|
+
return {
|
|
2434
|
+
entityKind: "Indeterminate",
|
|
2435
|
+
type: getLiteralType(num),
|
|
2436
|
+
};
|
|
1899
2437
|
}
|
|
1900
2438
|
function checkBooleanLiteral(bool) {
|
|
1901
|
-
return
|
|
2439
|
+
return {
|
|
2440
|
+
entityKind: "Indeterminate",
|
|
2441
|
+
type: getLiteralType(bool),
|
|
2442
|
+
};
|
|
1902
2443
|
}
|
|
1903
2444
|
function checkProgram() {
|
|
1904
2445
|
program.reportDuplicateSymbols(globalNamespaceNode.symbol.exports);
|
|
@@ -1947,7 +2488,7 @@ export function createChecker(program) {
|
|
|
1947
2488
|
}
|
|
1948
2489
|
function checkSourceFile(file) {
|
|
1949
2490
|
for (const statement of file.statements) {
|
|
1950
|
-
|
|
2491
|
+
checkNode(statement, undefined);
|
|
1951
2492
|
}
|
|
1952
2493
|
}
|
|
1953
2494
|
/**
|
|
@@ -2056,7 +2597,8 @@ export function createChecker(program) {
|
|
|
2056
2597
|
return false;
|
|
2057
2598
|
}
|
|
2058
2599
|
// Some of the mapper args are still template parameter so we shouldn't create the type.
|
|
2059
|
-
return !mapper.partial &&
|
|
2600
|
+
return (!mapper.partial &&
|
|
2601
|
+
mapper.args.every((t) => isValue(t) || t.entityKind === "Indeterminate" || t.kind !== "TemplateParameter"));
|
|
2060
2602
|
}
|
|
2061
2603
|
function checkModelExpression(node, mapper) {
|
|
2062
2604
|
const properties = createRekeyableMap();
|
|
@@ -2149,6 +2691,414 @@ export function createChecker(program) {
|
|
|
2149
2691
|
};
|
|
2150
2692
|
}
|
|
2151
2693
|
}
|
|
2694
|
+
function checkObjectValue(node, mapper, constraint) {
|
|
2695
|
+
const properties = checkObjectLiteralProperties(node, mapper);
|
|
2696
|
+
if (properties === null) {
|
|
2697
|
+
return null;
|
|
2698
|
+
}
|
|
2699
|
+
const preciseType = createTypeForObjectValue(node, properties);
|
|
2700
|
+
if (constraint && !checkTypeOfValueMatchConstraint(preciseType, constraint, node)) {
|
|
2701
|
+
return null;
|
|
2702
|
+
}
|
|
2703
|
+
return {
|
|
2704
|
+
entityKind: "Value",
|
|
2705
|
+
valueKind: "ObjectValue",
|
|
2706
|
+
node: node,
|
|
2707
|
+
properties,
|
|
2708
|
+
type: constraint ? constraint.type : preciseType,
|
|
2709
|
+
};
|
|
2710
|
+
}
|
|
2711
|
+
function createTypeForObjectValue(node, properties) {
|
|
2712
|
+
const model = createType({
|
|
2713
|
+
kind: "Model",
|
|
2714
|
+
name: "",
|
|
2715
|
+
node,
|
|
2716
|
+
properties: createRekeyableMap(),
|
|
2717
|
+
decorators: [],
|
|
2718
|
+
derivedModels: [],
|
|
2719
|
+
sourceModels: [],
|
|
2720
|
+
});
|
|
2721
|
+
for (const prop of properties.values()) {
|
|
2722
|
+
model.properties.set(prop.name, createModelPropertyForObjectPropertyDescriptor(prop, model));
|
|
2723
|
+
}
|
|
2724
|
+
return finishType(model);
|
|
2725
|
+
}
|
|
2726
|
+
function createModelPropertyForObjectPropertyDescriptor(prop, parentModel) {
|
|
2727
|
+
return createAndFinishType({
|
|
2728
|
+
kind: "ModelProperty",
|
|
2729
|
+
node: prop.node,
|
|
2730
|
+
model: parentModel,
|
|
2731
|
+
optional: false,
|
|
2732
|
+
name: prop.name,
|
|
2733
|
+
type: prop.value.type,
|
|
2734
|
+
decorators: [],
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
function checkObjectLiteralProperties(node, mapper) {
|
|
2738
|
+
const properties = new Map();
|
|
2739
|
+
let hasError = false;
|
|
2740
|
+
for (const prop of node.properties) {
|
|
2741
|
+
if ("id" in prop) {
|
|
2742
|
+
const value = getValueForNode(prop.value, mapper);
|
|
2743
|
+
if (value === null) {
|
|
2744
|
+
hasError = true;
|
|
2745
|
+
}
|
|
2746
|
+
else {
|
|
2747
|
+
properties.set(prop.id.sv, { name: prop.id.sv, value: value, node: prop });
|
|
2748
|
+
}
|
|
2749
|
+
}
|
|
2750
|
+
else {
|
|
2751
|
+
const targetType = checkObjectSpreadProperty(prop.target, mapper);
|
|
2752
|
+
if (targetType) {
|
|
2753
|
+
for (const [name, value] of targetType.properties) {
|
|
2754
|
+
properties.set(name, { ...value });
|
|
2755
|
+
}
|
|
2756
|
+
}
|
|
2757
|
+
}
|
|
2758
|
+
}
|
|
2759
|
+
return hasError ? null : properties;
|
|
2760
|
+
}
|
|
2761
|
+
function checkObjectSpreadProperty(targetNode, mapper) {
|
|
2762
|
+
const value = getValueForNode(targetNode, mapper);
|
|
2763
|
+
if (value === null) {
|
|
2764
|
+
return null;
|
|
2765
|
+
}
|
|
2766
|
+
if (value.valueKind !== "ObjectValue") {
|
|
2767
|
+
reportCheckerDiagnostic(createDiagnostic({ code: "spread-object", target: targetNode }));
|
|
2768
|
+
return null;
|
|
2769
|
+
}
|
|
2770
|
+
return value;
|
|
2771
|
+
}
|
|
2772
|
+
function checkArrayValue(node, mapper, constraint) {
|
|
2773
|
+
let hasError = false;
|
|
2774
|
+
const values = node.values.map((itemNode) => {
|
|
2775
|
+
const value = getValueForNode(itemNode, mapper);
|
|
2776
|
+
if (value === null) {
|
|
2777
|
+
hasError = true;
|
|
2778
|
+
}
|
|
2779
|
+
return value;
|
|
2780
|
+
});
|
|
2781
|
+
if (hasError) {
|
|
2782
|
+
return null;
|
|
2783
|
+
}
|
|
2784
|
+
const preciseType = createTypeForArrayValue(node, values);
|
|
2785
|
+
if (constraint && !checkTypeOfValueMatchConstraint(preciseType, constraint, node)) {
|
|
2786
|
+
return null;
|
|
2787
|
+
}
|
|
2788
|
+
return {
|
|
2789
|
+
entityKind: "Value",
|
|
2790
|
+
valueKind: "ArrayValue",
|
|
2791
|
+
node: node,
|
|
2792
|
+
values: values,
|
|
2793
|
+
type: constraint ? constraint.type : preciseType,
|
|
2794
|
+
};
|
|
2795
|
+
}
|
|
2796
|
+
function createTypeForArrayValue(node, values) {
|
|
2797
|
+
return createAndFinishType({
|
|
2798
|
+
kind: "Tuple",
|
|
2799
|
+
node,
|
|
2800
|
+
values: values.map((x) => x.type),
|
|
2801
|
+
});
|
|
2802
|
+
}
|
|
2803
|
+
function inferScalarForPrimitiveValue(type, literalType) {
|
|
2804
|
+
if (type === undefined) {
|
|
2805
|
+
return undefined;
|
|
2806
|
+
}
|
|
2807
|
+
switch (type.kind) {
|
|
2808
|
+
case "Scalar":
|
|
2809
|
+
if (ignoreDiagnostics(isTypeAssignableTo(literalType, type, literalType))) {
|
|
2810
|
+
return type;
|
|
2811
|
+
}
|
|
2812
|
+
return undefined;
|
|
2813
|
+
case "Union":
|
|
2814
|
+
let found = undefined;
|
|
2815
|
+
for (const variant of type.variants.values()) {
|
|
2816
|
+
const scalar = inferScalarForPrimitiveValue(variant.type, literalType);
|
|
2817
|
+
if (scalar) {
|
|
2818
|
+
if (found) {
|
|
2819
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2820
|
+
code: "ambiguous-scalar-type",
|
|
2821
|
+
format: {
|
|
2822
|
+
value: getTypeName(literalType),
|
|
2823
|
+
types: [found, scalar].map((x) => x.name).join(", "),
|
|
2824
|
+
example: found.name,
|
|
2825
|
+
},
|
|
2826
|
+
target: literalType,
|
|
2827
|
+
}));
|
|
2828
|
+
return undefined;
|
|
2829
|
+
}
|
|
2830
|
+
else {
|
|
2831
|
+
found = scalar;
|
|
2832
|
+
}
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
return found;
|
|
2836
|
+
default:
|
|
2837
|
+
return undefined;
|
|
2838
|
+
}
|
|
2839
|
+
}
|
|
2840
|
+
function checkStringValue(literalType, constraint, node) {
|
|
2841
|
+
if (constraint && !checkTypeOfValueMatchConstraint(literalType, constraint, node)) {
|
|
2842
|
+
return null;
|
|
2843
|
+
}
|
|
2844
|
+
let value;
|
|
2845
|
+
if (literalType.kind === "StringTemplate") {
|
|
2846
|
+
if (literalType.stringValue) {
|
|
2847
|
+
value = literalType.stringValue;
|
|
2848
|
+
}
|
|
2849
|
+
else {
|
|
2850
|
+
reportCheckerDiagnostics(explainStringTemplateNotSerializable(literalType));
|
|
2851
|
+
return null;
|
|
2852
|
+
}
|
|
2853
|
+
}
|
|
2854
|
+
else {
|
|
2855
|
+
value = literalType.value;
|
|
2856
|
+
}
|
|
2857
|
+
const scalar = inferScalarForPrimitiveValue(constraint?.type, literalType);
|
|
2858
|
+
return {
|
|
2859
|
+
entityKind: "Value",
|
|
2860
|
+
valueKind: "StringValue",
|
|
2861
|
+
value,
|
|
2862
|
+
type: constraint ? constraint.type : literalType,
|
|
2863
|
+
scalar,
|
|
2864
|
+
};
|
|
2865
|
+
}
|
|
2866
|
+
function checkNumericValue(literalType, constraint, node) {
|
|
2867
|
+
if (constraint && !checkTypeOfValueMatchConstraint(literalType, constraint, node)) {
|
|
2868
|
+
return null;
|
|
2869
|
+
}
|
|
2870
|
+
const scalar = inferScalarForPrimitiveValue(constraint?.type, literalType);
|
|
2871
|
+
return {
|
|
2872
|
+
entityKind: "Value",
|
|
2873
|
+
valueKind: "NumericValue",
|
|
2874
|
+
value: Numeric(literalType.valueAsString),
|
|
2875
|
+
type: constraint ? constraint.type : literalType,
|
|
2876
|
+
scalar,
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2879
|
+
function checkBooleanValue(literalType, constraint, node) {
|
|
2880
|
+
if (constraint && !checkTypeOfValueMatchConstraint(literalType, constraint, node)) {
|
|
2881
|
+
return null;
|
|
2882
|
+
}
|
|
2883
|
+
const scalar = inferScalarForPrimitiveValue(constraint?.type, literalType);
|
|
2884
|
+
return {
|
|
2885
|
+
entityKind: "Value",
|
|
2886
|
+
valueKind: "BooleanValue",
|
|
2887
|
+
value: literalType.value,
|
|
2888
|
+
type: constraint ? constraint.type : literalType,
|
|
2889
|
+
scalar,
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
function checkNullValue(literalType, constraint, node) {
|
|
2893
|
+
if (constraint && !checkTypeOfValueMatchConstraint(literalType, constraint, node)) {
|
|
2894
|
+
return null;
|
|
2895
|
+
}
|
|
2896
|
+
return {
|
|
2897
|
+
entityKind: "Value",
|
|
2898
|
+
valueKind: "NullValue",
|
|
2899
|
+
type: constraint ? constraint.type : literalType,
|
|
2900
|
+
value: null,
|
|
2901
|
+
};
|
|
2902
|
+
}
|
|
2903
|
+
function checkEnumValue(literalType, constraint, node) {
|
|
2904
|
+
if (constraint && !checkTypeOfValueMatchConstraint(literalType, constraint, node)) {
|
|
2905
|
+
return null;
|
|
2906
|
+
}
|
|
2907
|
+
return {
|
|
2908
|
+
entityKind: "Value",
|
|
2909
|
+
valueKind: "EnumValue",
|
|
2910
|
+
type: constraint ? constraint.type : literalType,
|
|
2911
|
+
value: literalType,
|
|
2912
|
+
};
|
|
2913
|
+
}
|
|
2914
|
+
function checkCallExpressionTarget(node, mapper) {
|
|
2915
|
+
const target = checkTypeReference(node.target, mapper);
|
|
2916
|
+
if (target.kind === "Scalar" || target.kind === "ScalarConstructor") {
|
|
2917
|
+
return target;
|
|
2918
|
+
}
|
|
2919
|
+
else {
|
|
2920
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2921
|
+
code: "non-callable",
|
|
2922
|
+
format: { type: target.kind },
|
|
2923
|
+
target: node.target,
|
|
2924
|
+
}));
|
|
2925
|
+
return null;
|
|
2926
|
+
}
|
|
2927
|
+
}
|
|
2928
|
+
/** Check the arguments of the call expression are a single value of the given syntax. */
|
|
2929
|
+
function checkPrimitiveArg(node, scalar, valueKind) {
|
|
2930
|
+
if (node.arguments.length !== 1) {
|
|
2931
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2932
|
+
code: "invalid-primitive-init",
|
|
2933
|
+
target: node.target,
|
|
2934
|
+
}));
|
|
2935
|
+
return null;
|
|
2936
|
+
}
|
|
2937
|
+
const argNode = node.arguments[0];
|
|
2938
|
+
const value = getValueForNode(argNode, undefined);
|
|
2939
|
+
if (value === null) {
|
|
2940
|
+
return null; // error should already have been reported above.
|
|
2941
|
+
}
|
|
2942
|
+
if (value.valueKind !== valueKind) {
|
|
2943
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2944
|
+
code: "invalid-primitive-init",
|
|
2945
|
+
messageId: "invalidArg",
|
|
2946
|
+
format: { actual: value.valueKind, expected: valueKind },
|
|
2947
|
+
target: argNode,
|
|
2948
|
+
}));
|
|
2949
|
+
return null;
|
|
2950
|
+
}
|
|
2951
|
+
if (!checkValueOfType(value, scalar, argNode)) {
|
|
2952
|
+
return null;
|
|
2953
|
+
}
|
|
2954
|
+
return { ...value, scalar, type: scalar };
|
|
2955
|
+
}
|
|
2956
|
+
function createScalarValue(node, mapper, declaration) {
|
|
2957
|
+
let hasError = false;
|
|
2958
|
+
const minArgs = declaration.parameters.filter((x) => !x.optional && !x.rest).length ?? 0;
|
|
2959
|
+
const maxArgs = declaration.parameters[declaration.parameters.length - 1]?.rest
|
|
2960
|
+
? undefined
|
|
2961
|
+
: declaration.parameters.length;
|
|
2962
|
+
if (node.arguments.length < minArgs ||
|
|
2963
|
+
(maxArgs !== undefined && node.arguments.length > maxArgs)) {
|
|
2964
|
+
// In the case we have too little args then this decorator is not applicable.
|
|
2965
|
+
// If there is too many args then we can still run the decorator as long as the args are valid.
|
|
2966
|
+
if (node.arguments.length < minArgs) {
|
|
2967
|
+
hasError = true;
|
|
2968
|
+
}
|
|
2969
|
+
if (maxArgs === undefined) {
|
|
2970
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2971
|
+
code: "invalid-argument-count",
|
|
2972
|
+
messageId: "atLeast",
|
|
2973
|
+
format: { actual: node.arguments.length.toString(), expected: minArgs.toString() },
|
|
2974
|
+
target: node,
|
|
2975
|
+
}));
|
|
2976
|
+
}
|
|
2977
|
+
else {
|
|
2978
|
+
const expected = minArgs === maxArgs ? minArgs.toString() : `${minArgs}-${maxArgs}`;
|
|
2979
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
2980
|
+
code: "invalid-argument-count",
|
|
2981
|
+
format: { actual: node.arguments.length.toString(), expected },
|
|
2982
|
+
target: node,
|
|
2983
|
+
}));
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
const resolvedArgs = [];
|
|
2987
|
+
for (const [index, parameter] of declaration.parameters.entries()) {
|
|
2988
|
+
if (parameter.rest) {
|
|
2989
|
+
const restType = getIndexType(parameter.type);
|
|
2990
|
+
if (restType) {
|
|
2991
|
+
for (let i = index; i < node.arguments.length; i++) {
|
|
2992
|
+
const argNode = node.arguments[i];
|
|
2993
|
+
if (argNode) {
|
|
2994
|
+
const arg = getValueForNode(argNode, mapper, { kind: "argument", type: restType });
|
|
2995
|
+
if (arg === null) {
|
|
2996
|
+
hasError = true;
|
|
2997
|
+
continue;
|
|
2998
|
+
}
|
|
2999
|
+
if (checkValueOfType(arg, restType, argNode)) {
|
|
3000
|
+
resolvedArgs.push(arg);
|
|
3001
|
+
}
|
|
3002
|
+
else {
|
|
3003
|
+
hasError = true;
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
}
|
|
3008
|
+
break;
|
|
3009
|
+
}
|
|
3010
|
+
const argNode = node.arguments[index];
|
|
3011
|
+
if (argNode) {
|
|
3012
|
+
const arg = getValueForNode(argNode, mapper, {
|
|
3013
|
+
kind: "argument",
|
|
3014
|
+
type: parameter.type,
|
|
3015
|
+
});
|
|
3016
|
+
if (arg === null) {
|
|
3017
|
+
hasError = true;
|
|
3018
|
+
continue;
|
|
3019
|
+
}
|
|
3020
|
+
if (checkValueOfType(arg, parameter.type, argNode)) {
|
|
3021
|
+
resolvedArgs.push(arg);
|
|
3022
|
+
}
|
|
3023
|
+
else {
|
|
3024
|
+
hasError = true;
|
|
3025
|
+
}
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
3028
|
+
if (hasError) {
|
|
3029
|
+
return null;
|
|
3030
|
+
}
|
|
3031
|
+
return {
|
|
3032
|
+
entityKind: "Value",
|
|
3033
|
+
valueKind: "ScalarValue",
|
|
3034
|
+
value: {
|
|
3035
|
+
name: declaration.name,
|
|
3036
|
+
args: resolvedArgs,
|
|
3037
|
+
},
|
|
3038
|
+
scalar: declaration.scalar,
|
|
3039
|
+
type: declaration.scalar,
|
|
3040
|
+
};
|
|
3041
|
+
}
|
|
3042
|
+
function checkCallExpression(node, mapper) {
|
|
3043
|
+
const target = checkCallExpressionTarget(node, mapper);
|
|
3044
|
+
if (target === null) {
|
|
3045
|
+
return null;
|
|
3046
|
+
}
|
|
3047
|
+
if (target.kind === "ScalarConstructor") {
|
|
3048
|
+
return createScalarValue(node, mapper, target);
|
|
3049
|
+
}
|
|
3050
|
+
if (areScalarsRelated(target, getStdType("string"))) {
|
|
3051
|
+
return checkPrimitiveArg(node, target, "StringValue");
|
|
3052
|
+
}
|
|
3053
|
+
else if (areScalarsRelated(target, getStdType("numeric"))) {
|
|
3054
|
+
return checkPrimitiveArg(node, target, "NumericValue");
|
|
3055
|
+
}
|
|
3056
|
+
else if (areScalarsRelated(target, getStdType("boolean"))) {
|
|
3057
|
+
return checkPrimitiveArg(node, target, "BooleanValue");
|
|
3058
|
+
}
|
|
3059
|
+
else {
|
|
3060
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
3061
|
+
code: "named-init-required",
|
|
3062
|
+
format: { typeKind: target.kind },
|
|
3063
|
+
target: node.target,
|
|
3064
|
+
}));
|
|
3065
|
+
return null;
|
|
3066
|
+
}
|
|
3067
|
+
}
|
|
3068
|
+
function checkTypeOfExpression(node, mapper) {
|
|
3069
|
+
const entity = checkNode(node.target, mapper, undefined);
|
|
3070
|
+
if (entity === null) {
|
|
3071
|
+
// Shouldn't need to emit error as we assume null value already emitted error when produced
|
|
3072
|
+
return errorType;
|
|
3073
|
+
}
|
|
3074
|
+
if (entity.entityKind === "Indeterminate") {
|
|
3075
|
+
return entity.type;
|
|
3076
|
+
}
|
|
3077
|
+
if (isType(entity)) {
|
|
3078
|
+
if (entity.kind === "TemplateParameter") {
|
|
3079
|
+
if (entity.constraint === undefined || entity.constraint.type !== undefined) {
|
|
3080
|
+
// means this template constraint will accept values
|
|
3081
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
3082
|
+
code: "expect-value",
|
|
3083
|
+
messageId: "templateConstraint",
|
|
3084
|
+
format: { name: getTypeName(entity) },
|
|
3085
|
+
target: node.target,
|
|
3086
|
+
}));
|
|
3087
|
+
return errorType;
|
|
3088
|
+
}
|
|
3089
|
+
else if (entity.constraint.valueType) {
|
|
3090
|
+
return entity.constraint.valueType;
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
3094
|
+
code: "expect-value",
|
|
3095
|
+
format: { name: getTypeName(entity) },
|
|
3096
|
+
target: node.target,
|
|
3097
|
+
}));
|
|
3098
|
+
return entity;
|
|
3099
|
+
}
|
|
3100
|
+
return entity.type;
|
|
3101
|
+
}
|
|
2152
3102
|
function createUnion(options) {
|
|
2153
3103
|
const variants = createRekeyableMap();
|
|
2154
3104
|
const union = createAndFinishType({
|
|
@@ -2229,6 +3179,15 @@ export function createChecker(program) {
|
|
|
2229
3179
|
}
|
|
2230
3180
|
}
|
|
2231
3181
|
break;
|
|
3182
|
+
case SyntaxKind.ScalarStatement:
|
|
3183
|
+
if (node.extends && node.extends.kind === SyntaxKind.TypeReference) {
|
|
3184
|
+
resolveAndCopyMembers(node.extends);
|
|
3185
|
+
}
|
|
3186
|
+
for (const member of node.members) {
|
|
3187
|
+
const name = member.id.sv;
|
|
3188
|
+
bindMember(name, member, 33554432 /* SymbolFlags.ScalarMember */);
|
|
3189
|
+
}
|
|
3190
|
+
break;
|
|
2232
3191
|
case SyntaxKind.ModelExpression:
|
|
2233
3192
|
for (const prop of node.properties) {
|
|
2234
3193
|
if (prop.kind === SyntaxKind.ModelSpreadProperty) {
|
|
@@ -2356,7 +3315,7 @@ export function createChecker(program) {
|
|
|
2356
3315
|
const sigTable = getOrCreateAugmentedSymbolTable(sig.metatypeMembers);
|
|
2357
3316
|
const sigParameterSym = sigTable.get("parameters");
|
|
2358
3317
|
if (sigParameterSym !== undefined) {
|
|
2359
|
-
const parametersSym = createSymbol(sigParameterSym.declarations[0], "parameters", 2 /* SymbolFlags.Model */ &
|
|
3318
|
+
const parametersSym = createSymbol(sigParameterSym.declarations[0], "parameters", 2 /* SymbolFlags.Model */ & 682 /* SymbolFlags.MemberContainer */);
|
|
2360
3319
|
copyMembersToContainer(parametersSym, sigParameterSym.members);
|
|
2361
3320
|
table.set("parameters", parametersSym);
|
|
2362
3321
|
table.set("returnType", sigTable.get("returnType"));
|
|
@@ -2381,17 +3340,17 @@ export function createChecker(program) {
|
|
|
2381
3340
|
return;
|
|
2382
3341
|
switch (type.kind) {
|
|
2383
3342
|
case "Model":
|
|
2384
|
-
type.symbol = createSymbol(type.node, type.name, 2 /* SymbolFlags.Model */ |
|
|
3343
|
+
type.symbol = createSymbol(type.node, type.name, 2 /* SymbolFlags.Model */ | 67108864 /* SymbolFlags.LateBound */);
|
|
2385
3344
|
mutate(type.symbol).type = type;
|
|
2386
3345
|
break;
|
|
2387
3346
|
case "Interface":
|
|
2388
|
-
type.symbol = createSymbol(type.node, type.name, 128 /* SymbolFlags.Interface */ |
|
|
3347
|
+
type.symbol = createSymbol(type.node, type.name, 128 /* SymbolFlags.Interface */ | 67108864 /* SymbolFlags.LateBound */);
|
|
2389
3348
|
mutate(type.symbol).type = type;
|
|
2390
3349
|
break;
|
|
2391
3350
|
case "Union":
|
|
2392
3351
|
if (!type.name)
|
|
2393
3352
|
return; // don't make a symbol for anonymous unions
|
|
2394
|
-
type.symbol = createSymbol(type.node, type.name, 512 /* SymbolFlags.Union */ |
|
|
3353
|
+
type.symbol = createSymbol(type.node, type.name, 512 /* SymbolFlags.Union */ | 67108864 /* SymbolFlags.LateBound */);
|
|
2395
3354
|
mutate(type.symbol).type = type;
|
|
2396
3355
|
break;
|
|
2397
3356
|
}
|
|
@@ -2404,6 +3363,11 @@ export function createChecker(program) {
|
|
|
2404
3363
|
lateBindMember(prop, 4 /* SymbolFlags.ModelProperty */);
|
|
2405
3364
|
}
|
|
2406
3365
|
break;
|
|
3366
|
+
case "Scalar":
|
|
3367
|
+
for (const member of type.constructors.values()) {
|
|
3368
|
+
lateBindMember(member, 33555780 /* SymbolFlags.Member */);
|
|
3369
|
+
}
|
|
3370
|
+
break;
|
|
2407
3371
|
case "Enum":
|
|
2408
3372
|
for (const member of type.members.values()) {
|
|
2409
3373
|
lateBindMember(member, 64 /* SymbolFlags.EnumMember */);
|
|
@@ -2425,7 +3389,7 @@ export function createChecker(program) {
|
|
|
2425
3389
|
// don't bind anything for union expressions
|
|
2426
3390
|
return;
|
|
2427
3391
|
}
|
|
2428
|
-
const sym = createSymbol(member.node, member.name, kind |
|
|
3392
|
+
const sym = createSymbol(member.node, member.name, kind | 67108864 /* SymbolFlags.LateBound */, containerSym);
|
|
2429
3393
|
mutate(sym).type = member;
|
|
2430
3394
|
compilerAssert(containerSym.members, "containerSym.members is undefined");
|
|
2431
3395
|
containerMembers ??= getOrCreateAugmentedSymbolTable(containerSym.members);
|
|
@@ -2611,7 +3575,14 @@ export function createChecker(program) {
|
|
|
2611
3575
|
else {
|
|
2612
3576
|
pendingResolutions.start(symId, ResolutionKind.Type);
|
|
2613
3577
|
type.type = getTypeForNode(prop.value, mapper);
|
|
2614
|
-
|
|
3578
|
+
if (prop.default) {
|
|
3579
|
+
const defaultValue = checkDefaultValue(prop.default, type.type);
|
|
3580
|
+
if (defaultValue !== null) {
|
|
3581
|
+
type.defaultValue = defaultValue;
|
|
3582
|
+
// eslint-disable-next-line deprecation/deprecation
|
|
3583
|
+
type.default = checkLegacyDefault(prop.default);
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
2615
3586
|
if (links) {
|
|
2616
3587
|
linkType(links, type, mapper);
|
|
2617
3588
|
}
|
|
@@ -2641,43 +3612,42 @@ export function createChecker(program) {
|
|
|
2641
3612
|
],
|
|
2642
3613
|
};
|
|
2643
3614
|
}
|
|
2644
|
-
function
|
|
2645
|
-
if (type === nullType) {
|
|
2646
|
-
return true;
|
|
2647
|
-
}
|
|
2648
|
-
if (type.kind === "StringTemplate") {
|
|
2649
|
-
const [valid] = isStringTemplateSerializable(type);
|
|
2650
|
-
return valid;
|
|
2651
|
-
}
|
|
2652
|
-
if (type.kind === "UnionVariant") {
|
|
2653
|
-
return isValueType(type.type);
|
|
2654
|
-
}
|
|
2655
|
-
const valueTypes = new Set(["String", "Number", "Boolean", "EnumMember", "Tuple"]);
|
|
2656
|
-
return valueTypes.has(type.kind);
|
|
2657
|
-
}
|
|
2658
|
-
function checkDefault(defaultNode, type) {
|
|
2659
|
-
const defaultType = getTypeForNode(defaultNode, undefined);
|
|
3615
|
+
function checkDefaultValue(defaultNode, type) {
|
|
2660
3616
|
if (isErrorType(type)) {
|
|
2661
|
-
|
|
3617
|
+
// if the prop type is an error we don't need to validate again.
|
|
3618
|
+
return null;
|
|
2662
3619
|
}
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
return errorType;
|
|
3620
|
+
const defaultValue = getValueForNode(defaultNode, undefined, {
|
|
3621
|
+
kind: "assignment",
|
|
3622
|
+
type,
|
|
3623
|
+
}, { legacyTupleAndModelCast: true });
|
|
3624
|
+
if (defaultValue === null) {
|
|
3625
|
+
return null;
|
|
2670
3626
|
}
|
|
2671
|
-
const [related, diagnostics] =
|
|
3627
|
+
const [related, diagnostics] = isValueOfType(defaultValue, type, defaultNode);
|
|
2672
3628
|
if (!related) {
|
|
2673
3629
|
reportCheckerDiagnostics(diagnostics);
|
|
2674
|
-
return
|
|
3630
|
+
return null;
|
|
2675
3631
|
}
|
|
2676
3632
|
else {
|
|
2677
|
-
return
|
|
3633
|
+
return { ...defaultValue, type };
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
/**
|
|
3637
|
+
* Fill in the legacy `.default` property.
|
|
3638
|
+
* We do do checking here we just keep existing behavior.
|
|
3639
|
+
*/
|
|
3640
|
+
function checkLegacyDefault(defaultNode) {
|
|
3641
|
+
const resolved = checkNode(defaultNode, undefined);
|
|
3642
|
+
if (resolved === null || isValue(resolved)) {
|
|
3643
|
+
return undefined;
|
|
2678
3644
|
}
|
|
3645
|
+
if (resolved.entityKind === "Indeterminate") {
|
|
3646
|
+
return resolved.type;
|
|
3647
|
+
}
|
|
3648
|
+
return resolved;
|
|
2679
3649
|
}
|
|
2680
|
-
function
|
|
3650
|
+
function checkDecoratorApplication(targetType, decNode, mapper) {
|
|
2681
3651
|
const sym = resolveTypeReferenceSym(decNode.target, undefined, true);
|
|
2682
3652
|
if (!sym) {
|
|
2683
3653
|
reportCheckerDiagnostic(createDiagnostic({
|
|
@@ -2695,7 +3665,6 @@ export function createChecker(program) {
|
|
|
2695
3665
|
return undefined;
|
|
2696
3666
|
}
|
|
2697
3667
|
const symbolLinks = getSymbolLinks(sym);
|
|
2698
|
-
let args = checkDecoratorArguments(decNode, mapper);
|
|
2699
3668
|
let hasError = false;
|
|
2700
3669
|
if (symbolLinks.declaredType === undefined) {
|
|
2701
3670
|
const decoratorDeclNode = sym.declarations.find((x) => x.kind === SyntaxKind.DecoratorDeclarationStatement);
|
|
@@ -2705,10 +3674,12 @@ export function createChecker(program) {
|
|
|
2705
3674
|
}
|
|
2706
3675
|
if (symbolLinks.declaredType) {
|
|
2707
3676
|
compilerAssert(symbolLinks.declaredType.kind === "Decorator", "Expected to find a decorator type.");
|
|
2708
|
-
|
|
2709
|
-
|
|
3677
|
+
if (!checkDecoratorTarget(targetType, symbolLinks.declaredType, decNode)) {
|
|
3678
|
+
hasError = true;
|
|
3679
|
+
}
|
|
2710
3680
|
}
|
|
2711
|
-
|
|
3681
|
+
const [argsHaveError, args] = checkDecoratorArguments(decNode, mapper, symbolLinks.declaredType);
|
|
3682
|
+
if (hasError || argsHaveError) {
|
|
2712
3683
|
return undefined;
|
|
2713
3684
|
}
|
|
2714
3685
|
return {
|
|
@@ -2718,62 +3689,121 @@ export function createChecker(program) {
|
|
|
2718
3689
|
args,
|
|
2719
3690
|
};
|
|
2720
3691
|
}
|
|
2721
|
-
function
|
|
2722
|
-
|
|
3692
|
+
function resolveDecoratorArgMarshalling(declaredType) {
|
|
3693
|
+
if (declaredType) {
|
|
3694
|
+
const location = getLocationContext(program, declaredType);
|
|
3695
|
+
if (location.type === "compiler") {
|
|
3696
|
+
return "new";
|
|
3697
|
+
}
|
|
3698
|
+
else if ((location.type === "library" || location.type === "project") &&
|
|
3699
|
+
location.flags?.decoratorArgMarshalling) {
|
|
3700
|
+
return location.flags.decoratorArgMarshalling;
|
|
3701
|
+
}
|
|
3702
|
+
else {
|
|
3703
|
+
return "legacy";
|
|
3704
|
+
}
|
|
3705
|
+
}
|
|
3706
|
+
return "new";
|
|
3707
|
+
}
|
|
3708
|
+
/** Check the decorator target is valid */
|
|
3709
|
+
function checkDecoratorTarget(targetType, declaration, decoratorNode) {
|
|
2723
3710
|
const [targetValid] = isTypeAssignableTo(targetType, declaration.target.type, decoratorNode);
|
|
2724
3711
|
if (!targetValid) {
|
|
2725
|
-
hasError = true;
|
|
2726
3712
|
reportCheckerDiagnostic(createDiagnostic({
|
|
2727
3713
|
code: "decorator-wrong-target",
|
|
2728
3714
|
messageId: "withExpected",
|
|
2729
3715
|
format: {
|
|
2730
3716
|
decorator: declaration.name,
|
|
2731
3717
|
to: getTypeName(targetType),
|
|
2732
|
-
expected:
|
|
3718
|
+
expected: getEntityName(declaration.target.type),
|
|
2733
3719
|
},
|
|
2734
3720
|
target: decoratorNode,
|
|
2735
3721
|
}));
|
|
2736
3722
|
}
|
|
2737
|
-
|
|
3723
|
+
return targetValid;
|
|
3724
|
+
}
|
|
3725
|
+
function checkDecoratorArguments(node, mapper, declaration) {
|
|
3726
|
+
// if we don't have a declaration we can just return the types or values if
|
|
3727
|
+
if (declaration === undefined) {
|
|
3728
|
+
return [
|
|
3729
|
+
false,
|
|
3730
|
+
node.arguments.map((argNode) => {
|
|
3731
|
+
let type = checkNode(argNode, mapper) ?? errorType;
|
|
3732
|
+
if (type.entityKind === "Indeterminate") {
|
|
3733
|
+
type = type.type;
|
|
3734
|
+
}
|
|
3735
|
+
return {
|
|
3736
|
+
value: type,
|
|
3737
|
+
jsValue: type,
|
|
3738
|
+
node: argNode,
|
|
3739
|
+
};
|
|
3740
|
+
}),
|
|
3741
|
+
];
|
|
3742
|
+
}
|
|
3743
|
+
let hasError = false;
|
|
3744
|
+
const minArgs = declaration.parameters.filter((x) => !x.optional && !x.rest).length ?? 0;
|
|
2738
3745
|
const maxArgs = declaration.parameters[declaration.parameters.length - 1]?.rest
|
|
2739
3746
|
? undefined
|
|
2740
3747
|
: declaration.parameters.length;
|
|
2741
|
-
if (
|
|
3748
|
+
if (node.arguments.length < minArgs ||
|
|
3749
|
+
(maxArgs !== undefined && node.arguments.length > maxArgs)) {
|
|
2742
3750
|
// In the case we have too little args then this decorator is not applicable.
|
|
2743
3751
|
// If there is too many args then we can still run the decorator as long as the args are valid.
|
|
2744
|
-
if (
|
|
3752
|
+
if (node.arguments.length < minArgs) {
|
|
2745
3753
|
hasError = true;
|
|
2746
3754
|
}
|
|
2747
3755
|
if (maxArgs === undefined) {
|
|
2748
3756
|
reportCheckerDiagnostic(createDiagnostic({
|
|
2749
3757
|
code: "invalid-argument-count",
|
|
2750
3758
|
messageId: "atLeast",
|
|
2751
|
-
format: { actual:
|
|
2752
|
-
target:
|
|
3759
|
+
format: { actual: node.arguments.length.toString(), expected: minArgs.toString() },
|
|
3760
|
+
target: node,
|
|
2753
3761
|
}));
|
|
2754
3762
|
}
|
|
2755
3763
|
else {
|
|
2756
3764
|
const expected = minArgs === maxArgs ? minArgs.toString() : `${minArgs}-${maxArgs}`;
|
|
2757
3765
|
reportCheckerDiagnostic(createDiagnostic({
|
|
2758
3766
|
code: "invalid-argument-count",
|
|
2759
|
-
format: { actual:
|
|
2760
|
-
target:
|
|
3767
|
+
format: { actual: node.arguments.length.toString(), expected },
|
|
3768
|
+
target: node,
|
|
2761
3769
|
}));
|
|
2762
3770
|
}
|
|
2763
3771
|
}
|
|
2764
3772
|
const resolvedArgs = [];
|
|
3773
|
+
const jsMarshalling = resolveDecoratorArgMarshalling(declaration);
|
|
3774
|
+
function resolveArg(argNode, perParamType) {
|
|
3775
|
+
const arg = getTypeOrValueForNode(argNode, mapper, {
|
|
3776
|
+
kind: "argument",
|
|
3777
|
+
constraint: perParamType,
|
|
3778
|
+
});
|
|
3779
|
+
if (arg !== null &&
|
|
3780
|
+
!(isType(arg) && isErrorType(arg)) &&
|
|
3781
|
+
checkArgumentAssignable(arg, perParamType, argNode)) {
|
|
3782
|
+
return {
|
|
3783
|
+
value: arg,
|
|
3784
|
+
node: argNode,
|
|
3785
|
+
jsValue: resolveDecoratorArgJsValue(arg, extractValueOfConstraints({
|
|
3786
|
+
kind: "argument",
|
|
3787
|
+
constraint: perParamType,
|
|
3788
|
+
}), jsMarshalling),
|
|
3789
|
+
};
|
|
3790
|
+
}
|
|
3791
|
+
else {
|
|
3792
|
+
return undefined;
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
2765
3795
|
for (const [index, parameter] of declaration.parameters.entries()) {
|
|
2766
3796
|
if (parameter.rest) {
|
|
2767
|
-
const restType =
|
|
3797
|
+
const restType = extractRestParamConstraint(parameter.type);
|
|
2768
3798
|
if (restType) {
|
|
2769
|
-
for (let i = index; i <
|
|
2770
|
-
const
|
|
2771
|
-
if (
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
}
|
|
2776
|
-
|
|
3799
|
+
for (let i = index; i < node.arguments.length; i++) {
|
|
3800
|
+
const argNode = node.arguments[i];
|
|
3801
|
+
if (argNode) {
|
|
3802
|
+
const arg = resolveArg(argNode, restType);
|
|
3803
|
+
if (arg) {
|
|
3804
|
+
resolvedArgs.push(arg);
|
|
3805
|
+
}
|
|
3806
|
+
else {
|
|
2777
3807
|
hasError = true;
|
|
2778
3808
|
}
|
|
2779
3809
|
}
|
|
@@ -2781,29 +3811,58 @@ export function createChecker(program) {
|
|
|
2781
3811
|
}
|
|
2782
3812
|
break;
|
|
2783
3813
|
}
|
|
2784
|
-
const
|
|
2785
|
-
if (
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
}
|
|
2790
|
-
|
|
3814
|
+
const argNode = node.arguments[index];
|
|
3815
|
+
if (argNode) {
|
|
3816
|
+
const arg = resolveArg(argNode, parameter.type);
|
|
3817
|
+
if (arg) {
|
|
3818
|
+
resolvedArgs.push(arg);
|
|
3819
|
+
}
|
|
3820
|
+
else {
|
|
2791
3821
|
hasError = true;
|
|
2792
3822
|
}
|
|
2793
3823
|
}
|
|
2794
3824
|
}
|
|
2795
3825
|
return [hasError, resolvedArgs];
|
|
2796
3826
|
}
|
|
3827
|
+
/** For a rest param of constraint T[] or valueof T[] return the T or valueof T */
|
|
3828
|
+
function extractRestParamConstraint(constraint) {
|
|
3829
|
+
let valueType;
|
|
3830
|
+
let type;
|
|
3831
|
+
if (constraint.valueType) {
|
|
3832
|
+
if (constraint.valueType.kind === "Model" &&
|
|
3833
|
+
isArrayModelType(program, constraint.valueType)) {
|
|
3834
|
+
valueType = constraint.valueType.indexer.value;
|
|
3835
|
+
}
|
|
3836
|
+
else {
|
|
3837
|
+
return undefined;
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
if (constraint.type) {
|
|
3841
|
+
if (constraint.type.kind === "Model" && isArrayModelType(program, constraint.type)) {
|
|
3842
|
+
type = constraint.type.indexer.value;
|
|
3843
|
+
}
|
|
3844
|
+
else {
|
|
3845
|
+
return undefined;
|
|
3846
|
+
}
|
|
3847
|
+
}
|
|
3848
|
+
return {
|
|
3849
|
+
entityKind: "MixedParameterConstraint",
|
|
3850
|
+
type,
|
|
3851
|
+
valueType,
|
|
3852
|
+
};
|
|
3853
|
+
}
|
|
2797
3854
|
function getIndexType(type) {
|
|
2798
3855
|
return type.kind === "Model" ? type.indexer?.value : undefined;
|
|
2799
3856
|
}
|
|
2800
|
-
function resolveDecoratorArgJsValue(value,
|
|
2801
|
-
if (
|
|
2802
|
-
if (value
|
|
2803
|
-
return
|
|
3857
|
+
function resolveDecoratorArgJsValue(value, valueConstraint, jsMarshalling) {
|
|
3858
|
+
if (valueConstraint !== undefined) {
|
|
3859
|
+
if (isValue(value)) {
|
|
3860
|
+
return jsMarshalling === "legacy"
|
|
3861
|
+
? legacyMarshallTypeForJS(checker, value)
|
|
3862
|
+
: marshallTypeForJS(value, valueConstraint.type);
|
|
2804
3863
|
}
|
|
2805
|
-
else
|
|
2806
|
-
return
|
|
3864
|
+
else {
|
|
3865
|
+
return value;
|
|
2807
3866
|
}
|
|
2808
3867
|
}
|
|
2809
3868
|
return value;
|
|
@@ -2814,8 +3873,8 @@ export function createChecker(program) {
|
|
|
2814
3873
|
reportCheckerDiagnostic(createDiagnostic({
|
|
2815
3874
|
code: "invalid-argument",
|
|
2816
3875
|
format: {
|
|
2817
|
-
value:
|
|
2818
|
-
expected:
|
|
3876
|
+
value: getEntityName(argumentType),
|
|
3877
|
+
expected: getEntityName(parameterType),
|
|
2819
3878
|
},
|
|
2820
3879
|
target: diagnosticTarget,
|
|
2821
3880
|
}));
|
|
@@ -2826,7 +3885,7 @@ export function createChecker(program) {
|
|
|
2826
3885
|
const augmentDecoratorNodes = augmentDecoratorsForSym.get(sym) ?? [];
|
|
2827
3886
|
const decorators = [];
|
|
2828
3887
|
for (const decNode of augmentDecoratorNodes) {
|
|
2829
|
-
const decorator =
|
|
3888
|
+
const decorator = checkDecoratorApplication(targetType, decNode, mapper);
|
|
2830
3889
|
if (decorator) {
|
|
2831
3890
|
decorators.unshift(decorator);
|
|
2832
3891
|
}
|
|
@@ -2842,7 +3901,7 @@ export function createChecker(program) {
|
|
|
2842
3901
|
...node.decorators,
|
|
2843
3902
|
];
|
|
2844
3903
|
for (const decNode of decoratorNodes) {
|
|
2845
|
-
const decorator =
|
|
3904
|
+
const decorator = checkDecoratorApplication(targetType, decNode, mapper);
|
|
2846
3905
|
if (decorator) {
|
|
2847
3906
|
decorators.unshift(decorator);
|
|
2848
3907
|
}
|
|
@@ -2863,16 +3922,6 @@ export function createChecker(program) {
|
|
|
2863
3922
|
}
|
|
2864
3923
|
return decorators;
|
|
2865
3924
|
}
|
|
2866
|
-
function checkDecoratorArguments(decorator, mapper) {
|
|
2867
|
-
return decorator.arguments.map((argNode) => {
|
|
2868
|
-
const type = getTypeForNode(argNode, mapper);
|
|
2869
|
-
return {
|
|
2870
|
-
value: type,
|
|
2871
|
-
jsValue: type,
|
|
2872
|
-
node: argNode,
|
|
2873
|
-
};
|
|
2874
|
-
});
|
|
2875
|
-
}
|
|
2876
3925
|
function checkScalar(node, mapper) {
|
|
2877
3926
|
const links = getSymbolLinks(node.symbol);
|
|
2878
3927
|
if (links.declaredType && mapper === undefined) {
|
|
@@ -2885,10 +3934,12 @@ export function createChecker(program) {
|
|
|
2885
3934
|
kind: "Scalar",
|
|
2886
3935
|
name: node.id.sv,
|
|
2887
3936
|
node: node,
|
|
3937
|
+
constructors: new Map(),
|
|
2888
3938
|
namespace: getParentNamespaceType(node),
|
|
2889
3939
|
decorators,
|
|
2890
3940
|
derivedScalars: [],
|
|
2891
3941
|
});
|
|
3942
|
+
checkScalarConstructors(type, node, type.constructors, mapper);
|
|
2892
3943
|
linkType(links, type, mapper);
|
|
2893
3944
|
if (node.extends) {
|
|
2894
3945
|
type.baseScalar = checkScalarExtends(node, node.extends, mapper);
|
|
@@ -2939,6 +3990,43 @@ export function createChecker(program) {
|
|
|
2939
3990
|
}
|
|
2940
3991
|
return extendsType;
|
|
2941
3992
|
}
|
|
3993
|
+
function checkScalarConstructors(parentScalar, node, constructors, mapper) {
|
|
3994
|
+
for (const member of node.members) {
|
|
3995
|
+
const constructor = checkScalarConstructor(member, mapper, parentScalar);
|
|
3996
|
+
if (constructors.has(constructor.name)) {
|
|
3997
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
3998
|
+
code: "constructor-duplicate",
|
|
3999
|
+
format: { name: constructor.name.toString() },
|
|
4000
|
+
target: member,
|
|
4001
|
+
}));
|
|
4002
|
+
continue;
|
|
4003
|
+
}
|
|
4004
|
+
constructors.set(constructor.name, constructor);
|
|
4005
|
+
}
|
|
4006
|
+
}
|
|
4007
|
+
function checkScalarConstructor(node, mapper, parentScalar) {
|
|
4008
|
+
const name = node.id.sv;
|
|
4009
|
+
const links = getSymbolLinksForMember(node);
|
|
4010
|
+
if (links && links.declaredType && mapper === undefined) {
|
|
4011
|
+
// we're not instantiating this scalar constructor and we've already checked it
|
|
4012
|
+
return links.declaredType;
|
|
4013
|
+
}
|
|
4014
|
+
const member = createType({
|
|
4015
|
+
kind: "ScalarConstructor",
|
|
4016
|
+
scalar: parentScalar,
|
|
4017
|
+
name,
|
|
4018
|
+
node,
|
|
4019
|
+
parameters: node.parameters.map((x) => checkFunctionParameter(x, mapper, false)),
|
|
4020
|
+
});
|
|
4021
|
+
linkMapper(member, mapper);
|
|
4022
|
+
if (shouldCreateTypeForTemplate(node.parent, mapper)) {
|
|
4023
|
+
finishType(member);
|
|
4024
|
+
}
|
|
4025
|
+
if (links) {
|
|
4026
|
+
linkType(links, member, mapper);
|
|
4027
|
+
}
|
|
4028
|
+
return finishType(member);
|
|
4029
|
+
}
|
|
2942
4030
|
function checkAlias(node, mapper) {
|
|
2943
4031
|
const links = getSymbolLinks(node.symbol);
|
|
2944
4032
|
if (links.declaredType && mapper === undefined) {
|
|
@@ -2959,10 +4047,55 @@ export function createChecker(program) {
|
|
|
2959
4047
|
}
|
|
2960
4048
|
pendingResolutions.start(aliasSymId, ResolutionKind.Type);
|
|
2961
4049
|
const type = getTypeForNode(node.value, mapper);
|
|
2962
|
-
|
|
4050
|
+
if (!isValue(type)) {
|
|
4051
|
+
linkType(links, type, mapper);
|
|
4052
|
+
}
|
|
2963
4053
|
pendingResolutions.finish(aliasSymId, ResolutionKind.Type);
|
|
2964
4054
|
return type;
|
|
2965
4055
|
}
|
|
4056
|
+
function checkConst(node) {
|
|
4057
|
+
const links = getSymbolLinks(node.symbol);
|
|
4058
|
+
if (links.value !== undefined) {
|
|
4059
|
+
return links.value;
|
|
4060
|
+
}
|
|
4061
|
+
const type = node.type ? getTypeForNode(node.type, undefined) : undefined;
|
|
4062
|
+
const symId = getSymbolId(node.symbol);
|
|
4063
|
+
if (pendingResolutions.has(symId, ResolutionKind.Value)) {
|
|
4064
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
4065
|
+
code: "circular-const",
|
|
4066
|
+
format: { name: node.id.sv },
|
|
4067
|
+
target: node,
|
|
4068
|
+
}));
|
|
4069
|
+
return null;
|
|
4070
|
+
}
|
|
4071
|
+
pendingResolutions.start(symId, ResolutionKind.Value);
|
|
4072
|
+
const value = getValueForNode(node.value, undefined, type && { kind: "assignment", type });
|
|
4073
|
+
pendingResolutions.finish(symId, ResolutionKind.Value);
|
|
4074
|
+
if (value === null || (type && !checkValueOfType(value, type, node.id))) {
|
|
4075
|
+
links.value = null;
|
|
4076
|
+
return links.value;
|
|
4077
|
+
}
|
|
4078
|
+
links.value = type ? { ...value, type } : { ...value };
|
|
4079
|
+
return links.value;
|
|
4080
|
+
}
|
|
4081
|
+
function inferScalarsFromConstraints(value, type) {
|
|
4082
|
+
switch (value.valueKind) {
|
|
4083
|
+
case "BooleanValue":
|
|
4084
|
+
case "StringValue":
|
|
4085
|
+
case "NumericValue":
|
|
4086
|
+
if (value.scalar === undefined) {
|
|
4087
|
+
const scalar = inferScalarForPrimitiveValue(type, value.type);
|
|
4088
|
+
return { ...value, scalar };
|
|
4089
|
+
}
|
|
4090
|
+
return value;
|
|
4091
|
+
case "ArrayValue":
|
|
4092
|
+
case "ObjectValue":
|
|
4093
|
+
case "EnumValue":
|
|
4094
|
+
case "NullValue":
|
|
4095
|
+
case "ScalarValue":
|
|
4096
|
+
return value;
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
2966
4099
|
function checkEnum(node, mapper) {
|
|
2967
4100
|
const links = getSymbolLinks(node.symbol);
|
|
2968
4101
|
if (!links.type) {
|
|
@@ -3279,6 +4412,7 @@ export function createChecker(program) {
|
|
|
3279
4412
|
// If the type has an associated syntax node, check any directives that
|
|
3280
4413
|
// might be attached.
|
|
3281
4414
|
const createdType = typeDef;
|
|
4415
|
+
createdType.entityKind = "Type";
|
|
3282
4416
|
if (createdType.node) {
|
|
3283
4417
|
checkDirectives(createdType.node, createdType);
|
|
3284
4418
|
}
|
|
@@ -4024,6 +5158,7 @@ export function createChecker(program) {
|
|
|
4024
5158
|
kind: "Number",
|
|
4025
5159
|
value,
|
|
4026
5160
|
valueAsString,
|
|
5161
|
+
numericValue: Numeric(valueAsString),
|
|
4027
5162
|
});
|
|
4028
5163
|
break;
|
|
4029
5164
|
}
|
|
@@ -4036,7 +5171,7 @@ export function createChecker(program) {
|
|
|
4036
5171
|
throw new ProjectionError("Can't find decorator.");
|
|
4037
5172
|
compilerAssert(ref.flags & 16384 /* SymbolFlags.Decorator */, "should only resolve decorator symbols");
|
|
4038
5173
|
return createFunctionType((...args) => {
|
|
4039
|
-
ref.value({ program }, ...
|
|
5174
|
+
ref.value({ program }, ...args.map(unsafe_projectionArgumentMarshalForJS));
|
|
4040
5175
|
return voidType;
|
|
4041
5176
|
});
|
|
4042
5177
|
}
|
|
@@ -4061,7 +5196,7 @@ export function createChecker(program) {
|
|
|
4061
5196
|
else if (ref.flags & 131072 /* SymbolFlags.Function */) {
|
|
4062
5197
|
// TODO: store this in a symbol link probably?
|
|
4063
5198
|
const t = createFunctionType((...args) => {
|
|
4064
|
-
const retval = ref.value(program, ...
|
|
5199
|
+
const retval = ref.value(program, ...args.map(unsafe_projectionArgumentMarshalForJS));
|
|
4065
5200
|
return marshalProjectionReturn(retval, { functionName: node.sv });
|
|
4066
5201
|
});
|
|
4067
5202
|
return t;
|
|
@@ -4135,6 +5270,31 @@ export function createChecker(program) {
|
|
|
4135
5270
|
parts.push(current.sv);
|
|
4136
5271
|
return parts.reverse().join(".");
|
|
4137
5272
|
}
|
|
5273
|
+
/**
|
|
5274
|
+
* Check if the source type can be assigned to the target type and emit diagnostics
|
|
5275
|
+
* @param source Type of a value
|
|
5276
|
+
* @param constraint
|
|
5277
|
+
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5278
|
+
*/
|
|
5279
|
+
function checkTypeOfValueMatchConstraint(source, constraint, diagnosticTarget) {
|
|
5280
|
+
const [related, diagnostics] = isTypeAssignableTo(source, constraint.type, diagnosticTarget);
|
|
5281
|
+
if (!related) {
|
|
5282
|
+
if (constraint.kind === "argument") {
|
|
5283
|
+
reportCheckerDiagnostic(createDiagnostic({
|
|
5284
|
+
code: "invalid-argument",
|
|
5285
|
+
format: {
|
|
5286
|
+
value: getEntityName(source),
|
|
5287
|
+
expected: getEntityName(constraint.type),
|
|
5288
|
+
},
|
|
5289
|
+
target: diagnosticTarget,
|
|
5290
|
+
}));
|
|
5291
|
+
}
|
|
5292
|
+
else {
|
|
5293
|
+
reportCheckerDiagnostics(diagnostics);
|
|
5294
|
+
}
|
|
5295
|
+
}
|
|
5296
|
+
return related;
|
|
5297
|
+
}
|
|
4138
5298
|
/**
|
|
4139
5299
|
* Check if the source type can be assigned to the target type and emit diagnostics
|
|
4140
5300
|
* @param source Source type
|
|
@@ -4148,6 +5308,13 @@ export function createChecker(program) {
|
|
|
4148
5308
|
}
|
|
4149
5309
|
return related;
|
|
4150
5310
|
}
|
|
5311
|
+
function checkValueOfType(source, target, diagnosticTarget) {
|
|
5312
|
+
const [related, diagnostics] = isValueOfType(source, target, diagnosticTarget);
|
|
5313
|
+
if (!related) {
|
|
5314
|
+
reportCheckerDiagnostics(diagnostics);
|
|
5315
|
+
}
|
|
5316
|
+
return related;
|
|
5317
|
+
}
|
|
4151
5318
|
/**
|
|
4152
5319
|
* Check if the source type can be assigned to the target type.
|
|
4153
5320
|
* @param source Source type
|
|
@@ -4158,6 +5325,16 @@ export function createChecker(program) {
|
|
|
4158
5325
|
const [related, diagnostics] = isTypeAssignableToInternal(source, target, diagnosticTarget, new MultiKeyMap());
|
|
4159
5326
|
return [related === Related.true, diagnostics];
|
|
4160
5327
|
}
|
|
5328
|
+
/**
|
|
5329
|
+
* Check if the given Value type is of the given type.
|
|
5330
|
+
* @param source Value
|
|
5331
|
+
* @param target Target type
|
|
5332
|
+
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5333
|
+
*/
|
|
5334
|
+
function isValueOfType(source, target, diagnosticTarget) {
|
|
5335
|
+
const [related, diagnostics] = isValueOfTypeInternal(source, target, diagnosticTarget, new MultiKeyMap());
|
|
5336
|
+
return [related === Related.true, diagnostics];
|
|
5337
|
+
}
|
|
4161
5338
|
function isTypeAssignableToInternal(source, target, diagnosticTarget, relationCache) {
|
|
4162
5339
|
const cached = relationCache.get([source, target]);
|
|
4163
5340
|
if (cached !== undefined) {
|
|
@@ -4168,26 +5345,44 @@ export function createChecker(program) {
|
|
|
4168
5345
|
return [result, diagnostics];
|
|
4169
5346
|
}
|
|
4170
5347
|
function isTypeAssignableToWorker(source, target, diagnosticTarget, relationCache) {
|
|
4171
|
-
// BACKCOMPAT:
|
|
4172
|
-
if (
|
|
4173
|
-
|
|
5348
|
+
// BACKCOMPAT: Allow certain type to be accepted as values
|
|
5349
|
+
if ("kind" in source &&
|
|
5350
|
+
"entityKind" in target &&
|
|
5351
|
+
source.kind === "TemplateParameter" &&
|
|
5352
|
+
source.constraint?.type &&
|
|
5353
|
+
source.constraint.valueType === undefined &&
|
|
5354
|
+
target.entityKind === "MixedParameterConstraint" &&
|
|
5355
|
+
target.valueType) {
|
|
5356
|
+
const [assignable] = isTypeAssignableToInternal(source.constraint.type, target.valueType, diagnosticTarget, relationCache);
|
|
4174
5357
|
if (assignable) {
|
|
4175
|
-
const constraint =
|
|
4176
|
-
reportDeprecated(program, `Template constrainted to '${constraint}' will not be assignable to '${
|
|
5358
|
+
const constraint = getEntityName(source.constraint);
|
|
5359
|
+
reportDeprecated(program, `Template constrainted to '${constraint}' will not be assignable to '${getEntityName(target)}' in the future. Update the constraint to be 'valueof ${constraint}'`, diagnosticTarget);
|
|
4177
5360
|
return [Related.true, []];
|
|
4178
5361
|
}
|
|
4179
5362
|
}
|
|
4180
|
-
|
|
5363
|
+
if ("kind" in source && source.kind === "TemplateParameter") {
|
|
4181
5364
|
source = source.constraint ?? unknownType;
|
|
4182
5365
|
}
|
|
5366
|
+
if (target.entityKind === "Indeterminate") {
|
|
5367
|
+
target = target.type;
|
|
5368
|
+
}
|
|
4183
5369
|
if (source === target)
|
|
4184
5370
|
return [Related.true, []];
|
|
4185
|
-
if (target
|
|
4186
|
-
return
|
|
5371
|
+
if (isValue(target)) {
|
|
5372
|
+
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5373
|
+
}
|
|
5374
|
+
if (source.entityKind === "Indeterminate") {
|
|
5375
|
+
return isIndeterminateEntityAssignableTo(source, target, diagnosticTarget, relationCache);
|
|
4187
5376
|
}
|
|
4188
|
-
if (
|
|
5377
|
+
if (target.entityKind === "MixedParameterConstraint") {
|
|
5378
|
+
return isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache);
|
|
5379
|
+
}
|
|
5380
|
+
if (isValue(source) || (source.entityKind === "MixedParameterConstraint" && source.valueType)) {
|
|
4189
5381
|
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
4190
5382
|
}
|
|
5383
|
+
if (source.entityKind === "MixedParameterConstraint") {
|
|
5384
|
+
return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
|
|
5385
|
+
}
|
|
4191
5386
|
const isSimpleTypeRelated = isSimpleTypeAssignableTo(source, target);
|
|
4192
5387
|
if (isSimpleTypeRelated === true) {
|
|
4193
5388
|
return [Related.true, []];
|
|
@@ -4227,19 +5422,21 @@ export function createChecker(program) {
|
|
|
4227
5422
|
else if (target.kind === "Model" &&
|
|
4228
5423
|
isArrayModelType(program, target) &&
|
|
4229
5424
|
source.kind === "Model") {
|
|
4230
|
-
|
|
5425
|
+
if (isArrayModelType(program, source)) {
|
|
5426
|
+
return hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache);
|
|
5427
|
+
}
|
|
5428
|
+
else {
|
|
5429
|
+
// For other models just fallback to unassignable
|
|
5430
|
+
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5431
|
+
}
|
|
4231
5432
|
}
|
|
4232
5433
|
else if (target.kind === "Model" && source.kind === "Model") {
|
|
4233
5434
|
return isModelRelatedTo(source, target, diagnosticTarget, relationCache);
|
|
4234
5435
|
}
|
|
4235
|
-
else if (target.kind === "Model" &&
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
|
|
4239
|
-
return [Related.false, diagnostics];
|
|
4240
|
-
}
|
|
4241
|
-
}
|
|
4242
|
-
return [Related.true, []];
|
|
5436
|
+
else if (target.kind === "Model" &&
|
|
5437
|
+
isArrayModelType(program, target) &&
|
|
5438
|
+
source.kind === "Tuple") {
|
|
5439
|
+
return isTupleAssignableToArray(source, target, diagnosticTarget, relationCache);
|
|
4243
5440
|
}
|
|
4244
5441
|
else if (target.kind === "Tuple" && source.kind === "Tuple") {
|
|
4245
5442
|
return isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache);
|
|
@@ -4252,18 +5449,60 @@ export function createChecker(program) {
|
|
|
4252
5449
|
}
|
|
4253
5450
|
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
4254
5451
|
}
|
|
4255
|
-
function
|
|
4256
|
-
|
|
4257
|
-
|
|
5452
|
+
function isIndeterminateEntityAssignableTo(indeterminate, target, diagnosticTarget, relationCache) {
|
|
5453
|
+
const [typeRelated, typeDiagnostics] = isTypeAssignableToInternal(indeterminate.type, target, diagnosticTarget, relationCache);
|
|
5454
|
+
if (typeRelated) {
|
|
5455
|
+
return [Related.true, []];
|
|
4258
5456
|
}
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
5457
|
+
if (target.entityKind === "MixedParameterConstraint" && target.valueType) {
|
|
5458
|
+
const [valueRelated] = isTypeAssignableToInternal(indeterminate.type, target.valueType, diagnosticTarget, relationCache);
|
|
5459
|
+
if (valueRelated) {
|
|
5460
|
+
return [Related.true, []];
|
|
5461
|
+
}
|
|
4262
5462
|
}
|
|
4263
|
-
|
|
5463
|
+
return [Related.false, typeDiagnostics];
|
|
5464
|
+
}
|
|
5465
|
+
function isAssignableToValueType(source, target, diagnosticTarget, relationCache) {
|
|
5466
|
+
if (!isValue(source)) {
|
|
4264
5467
|
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
4265
5468
|
}
|
|
4266
|
-
return
|
|
5469
|
+
return isValueOfTypeInternal(source, target, diagnosticTarget, relationCache);
|
|
5470
|
+
}
|
|
5471
|
+
function isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache) {
|
|
5472
|
+
if ("entityKind" in source && source.entityKind === "MixedParameterConstraint") {
|
|
5473
|
+
if (source.type && target.type) {
|
|
5474
|
+
const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.type, target.type, diagnosticTarget, relationCache);
|
|
5475
|
+
if (variantAssignable === Related.false) {
|
|
5476
|
+
return [Related.false, diagnostics];
|
|
5477
|
+
}
|
|
5478
|
+
return [Related.true, []];
|
|
5479
|
+
}
|
|
5480
|
+
if (source.valueType && target.valueType) {
|
|
5481
|
+
const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.valueType, target.valueType, diagnosticTarget, relationCache);
|
|
5482
|
+
if (variantAssignable === Related.false) {
|
|
5483
|
+
return [Related.false, diagnostics];
|
|
5484
|
+
}
|
|
5485
|
+
return [Related.true, []];
|
|
5486
|
+
}
|
|
5487
|
+
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5488
|
+
}
|
|
5489
|
+
if (target.type) {
|
|
5490
|
+
const [related] = isTypeAssignableToInternal(source, target.type, diagnosticTarget, relationCache);
|
|
5491
|
+
if (related) {
|
|
5492
|
+
return [Related.true, []];
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
if (target.valueType) {
|
|
5496
|
+
const [related] = isAssignableToValueType(source, target.valueType, diagnosticTarget, relationCache);
|
|
5497
|
+
if (related) {
|
|
5498
|
+
return [Related.true, []];
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5502
|
+
}
|
|
5503
|
+
/** Check if the value is assignable to the given type. */
|
|
5504
|
+
function isValueOfTypeInternal(source, target, diagnosticTarget, relationCache) {
|
|
5505
|
+
return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
|
|
4267
5506
|
}
|
|
4268
5507
|
function isReflectionType(type) {
|
|
4269
5508
|
return (type.kind === "Model" &&
|
|
@@ -4276,7 +5515,7 @@ export function createChecker(program) {
|
|
|
4276
5515
|
return isNumericLiteralRelatedTo(source, target);
|
|
4277
5516
|
case "String":
|
|
4278
5517
|
case "StringTemplate":
|
|
4279
|
-
return
|
|
5518
|
+
return isStringLiteralRelatedTo(source, target);
|
|
4280
5519
|
case "Boolean":
|
|
4281
5520
|
return areScalarsRelated(target, getStdType("boolean"));
|
|
4282
5521
|
case "Scalar":
|
|
@@ -4314,7 +5553,12 @@ export function createChecker(program) {
|
|
|
4314
5553
|
return false;
|
|
4315
5554
|
}
|
|
4316
5555
|
if (target.kind === "String") {
|
|
4317
|
-
return source.kind === "String" &&
|
|
5556
|
+
return ((source.kind === "String" && source.value === target.value) ||
|
|
5557
|
+
(source.kind === "StringTemplate" && source.stringValue === target.value));
|
|
5558
|
+
}
|
|
5559
|
+
if (target.kind === "StringTemplate" && target.stringValue) {
|
|
5560
|
+
return ((source.kind === "String" && source.value === target.stringValue) ||
|
|
5561
|
+
(source.kind === "StringTemplate" && source.stringValue === target.stringValue));
|
|
4318
5562
|
}
|
|
4319
5563
|
if (target.kind === "Number") {
|
|
4320
5564
|
return source.kind === "Number" && target.value === source.value;
|
|
@@ -4322,6 +5566,29 @@ export function createChecker(program) {
|
|
|
4322
5566
|
return undefined;
|
|
4323
5567
|
}
|
|
4324
5568
|
function isNumericLiteralRelatedTo(source, target) {
|
|
5569
|
+
// First check that the source numeric literal is assignable to the target scalar
|
|
5570
|
+
if (!isNumericAssignableToNumericScalar(source.numericValue, target)) {
|
|
5571
|
+
return false;
|
|
5572
|
+
}
|
|
5573
|
+
const min = getMinValueAsNumeric(program, target);
|
|
5574
|
+
const max = getMaxValueAsNumeric(program, target);
|
|
5575
|
+
const minExclusive = getMinValueExclusiveAsNumeric(program, target);
|
|
5576
|
+
const maxExclusive = getMaxValueExclusiveAsNumeric(program, target);
|
|
5577
|
+
if (min && source.numericValue.lt(min)) {
|
|
5578
|
+
return false;
|
|
5579
|
+
}
|
|
5580
|
+
if (minExclusive && source.numericValue.lte(minExclusive)) {
|
|
5581
|
+
return false;
|
|
5582
|
+
}
|
|
5583
|
+
if (max && source.numericValue.gt(max)) {
|
|
5584
|
+
return false;
|
|
5585
|
+
}
|
|
5586
|
+
if (maxExclusive && source.numericValue.gte(maxExclusive)) {
|
|
5587
|
+
return false;
|
|
5588
|
+
}
|
|
5589
|
+
return true;
|
|
5590
|
+
}
|
|
5591
|
+
function isNumericAssignableToNumericScalar(source, target) {
|
|
4325
5592
|
// if the target does not derive from numeric, then it can't be assigned a numeric literal
|
|
4326
5593
|
if (!areScalarsRelated(target, getStdType("numeric"))) {
|
|
4327
5594
|
return false;
|
|
@@ -4338,7 +5605,7 @@ export function createChecker(program) {
|
|
|
4338
5605
|
return true;
|
|
4339
5606
|
if (target.name === "decimal128")
|
|
4340
5607
|
return true;
|
|
4341
|
-
const isInt =
|
|
5608
|
+
const isInt = source.isInteger;
|
|
4342
5609
|
if (target.name === "integer")
|
|
4343
5610
|
return isInt;
|
|
4344
5611
|
if (target.name === "float")
|
|
@@ -4346,7 +5613,25 @@ export function createChecker(program) {
|
|
|
4346
5613
|
if (!(target.name in numericRanges))
|
|
4347
5614
|
return false;
|
|
4348
5615
|
const [low, high, options] = numericRanges[target.name];
|
|
4349
|
-
return source.
|
|
5616
|
+
return source.gte(low) && source.lte(high) && (!options.int || isInt);
|
|
5617
|
+
}
|
|
5618
|
+
function isStringLiteralRelatedTo(source, target) {
|
|
5619
|
+
if (!areScalarsRelated(target, getStdType("string"))) {
|
|
5620
|
+
return false;
|
|
5621
|
+
}
|
|
5622
|
+
if (source.kind === "StringTemplate") {
|
|
5623
|
+
return true;
|
|
5624
|
+
}
|
|
5625
|
+
const len = source.value.length;
|
|
5626
|
+
const min = getMinLength(program, target);
|
|
5627
|
+
const max = getMaxLength(program, target);
|
|
5628
|
+
if (min && len < min) {
|
|
5629
|
+
return false;
|
|
5630
|
+
}
|
|
5631
|
+
if (max && len > max) {
|
|
5632
|
+
return false;
|
|
5633
|
+
}
|
|
5634
|
+
return true;
|
|
4350
5635
|
}
|
|
4351
5636
|
function isModelRelatedTo(source, target, diagnosticTarget, relationCache) {
|
|
4352
5637
|
relationCache.set([source, target], Related.maybe);
|
|
@@ -4396,8 +5681,30 @@ export function createChecker(program) {
|
|
|
4396
5681
|
}
|
|
4397
5682
|
}
|
|
4398
5683
|
}
|
|
5684
|
+
else if (shouldCheckExcessProperties(source)) {
|
|
5685
|
+
for (const [propName, prop] of remainingProperties) {
|
|
5686
|
+
if (shouldCheckExcessProperty(prop)) {
|
|
5687
|
+
diagnostics.push(createDiagnostic({
|
|
5688
|
+
code: "unexpected-property",
|
|
5689
|
+
format: {
|
|
5690
|
+
propertyName: propName,
|
|
5691
|
+
type: getEntityName(target),
|
|
5692
|
+
},
|
|
5693
|
+
target: prop,
|
|
5694
|
+
}));
|
|
5695
|
+
}
|
|
5696
|
+
}
|
|
5697
|
+
}
|
|
4399
5698
|
return [diagnostics.length === 0 ? Related.true : Related.false, diagnostics];
|
|
4400
5699
|
}
|
|
5700
|
+
/** If we should check for excess properties on the given model. */
|
|
5701
|
+
function shouldCheckExcessProperties(model) {
|
|
5702
|
+
return model.node?.kind === SyntaxKind.ObjectLiteral;
|
|
5703
|
+
}
|
|
5704
|
+
/** If we should check for this specific property */
|
|
5705
|
+
function shouldCheckExcessProperty(prop) {
|
|
5706
|
+
return (prop.node?.kind === SyntaxKind.ObjectLiteralProperty && prop.node.parent === prop.model?.node);
|
|
5707
|
+
}
|
|
4401
5708
|
function getProperty(model, name) {
|
|
4402
5709
|
return (model.properties.get(name) ??
|
|
4403
5710
|
(model.baseModel !== undefined ? getProperty(model.baseModel, name) : undefined));
|
|
@@ -4430,6 +5737,51 @@ export function createChecker(program) {
|
|
|
4430
5737
|
}
|
|
4431
5738
|
return isTypeAssignableToInternal(source.indexer.value, target.indexer.value, diagnosticTarget, relationCache);
|
|
4432
5739
|
}
|
|
5740
|
+
function isTupleAssignableToArray(source, target, diagnosticTarget, relationCache) {
|
|
5741
|
+
const minItems = getMinItems(program, target);
|
|
5742
|
+
const maxItems = getMaxItems(program, target);
|
|
5743
|
+
if (minItems !== undefined && source.values.length < minItems) {
|
|
5744
|
+
return [
|
|
5745
|
+
Related.false,
|
|
5746
|
+
[
|
|
5747
|
+
createDiagnostic({
|
|
5748
|
+
code: "unassignable",
|
|
5749
|
+
messageId: "withDetails",
|
|
5750
|
+
format: {
|
|
5751
|
+
sourceType: getEntityName(source),
|
|
5752
|
+
targetType: getTypeName(target),
|
|
5753
|
+
details: `Source has ${source.values.length} element(s) but target requires ${minItems}.`,
|
|
5754
|
+
},
|
|
5755
|
+
target: diagnosticTarget,
|
|
5756
|
+
}),
|
|
5757
|
+
],
|
|
5758
|
+
];
|
|
5759
|
+
}
|
|
5760
|
+
if (maxItems !== undefined && source.values.length > maxItems) {
|
|
5761
|
+
return [
|
|
5762
|
+
Related.false,
|
|
5763
|
+
[
|
|
5764
|
+
createDiagnostic({
|
|
5765
|
+
code: "unassignable",
|
|
5766
|
+
messageId: "withDetails",
|
|
5767
|
+
format: {
|
|
5768
|
+
sourceType: getEntityName(source),
|
|
5769
|
+
targetType: getTypeName(target),
|
|
5770
|
+
details: `Source has ${source.values.length} element(s) but target only allows ${maxItems}.`,
|
|
5771
|
+
},
|
|
5772
|
+
target: diagnosticTarget,
|
|
5773
|
+
}),
|
|
5774
|
+
],
|
|
5775
|
+
];
|
|
5776
|
+
}
|
|
5777
|
+
for (const item of source.values) {
|
|
5778
|
+
const [related, diagnostics] = isTypeAssignableToInternal(item, target.indexer.value, diagnosticTarget, relationCache);
|
|
5779
|
+
if (!related) {
|
|
5780
|
+
return [Related.false, diagnostics];
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
return [Related.true, []];
|
|
5784
|
+
}
|
|
4433
5785
|
function isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache) {
|
|
4434
5786
|
if (source.values.length !== target.values.length) {
|
|
4435
5787
|
return [
|
|
@@ -4439,7 +5791,7 @@ export function createChecker(program) {
|
|
|
4439
5791
|
code: "unassignable",
|
|
4440
5792
|
messageId: "withDetails",
|
|
4441
5793
|
format: {
|
|
4442
|
-
sourceType:
|
|
5794
|
+
sourceType: getEntityName(source),
|
|
4443
5795
|
targetType: getTypeName(target),
|
|
4444
5796
|
details: `Source has ${source.values.length} element(s) but target requires ${target.values.length}.`,
|
|
4445
5797
|
},
|
|
@@ -4492,7 +5844,7 @@ export function createChecker(program) {
|
|
|
4492
5844
|
function createUnassignableDiagnostic(source, target, diagnosticTarget) {
|
|
4493
5845
|
return createDiagnostic({
|
|
4494
5846
|
code: "unassignable",
|
|
4495
|
-
format: { targetType:
|
|
5847
|
+
format: { targetType: getEntityName(target), value: getEntityName(source) },
|
|
4496
5848
|
target: diagnosticTarget,
|
|
4497
5849
|
});
|
|
4498
5850
|
}
|
|
@@ -4516,22 +5868,6 @@ export function createChecker(program) {
|
|
|
4516
5868
|
function isAnonymous(type) {
|
|
4517
5869
|
return !("name" in type) || typeof type.name !== "string" || !type.name;
|
|
4518
5870
|
}
|
|
4519
|
-
function isErrorType(type) {
|
|
4520
|
-
return type.kind === "Intrinsic" && type.name === "ErrorType";
|
|
4521
|
-
}
|
|
4522
|
-
const numericRanges = {
|
|
4523
|
-
int64: [BigInt("-9223372036854775807"), BigInt("9223372036854775808"), { int: true }],
|
|
4524
|
-
int32: [-2147483648, 2147483647, { int: true }],
|
|
4525
|
-
int16: [-32768, 32767, { int: true }],
|
|
4526
|
-
int8: [-128, 127, { int: true }],
|
|
4527
|
-
uint64: [0, BigInt("18446744073709551615"), { int: true }],
|
|
4528
|
-
uint32: [0, 4294967295, { int: true }],
|
|
4529
|
-
uint16: [0, 65535, { int: true }],
|
|
4530
|
-
uint8: [0, 255, { int: true }],
|
|
4531
|
-
safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER, { int: true }],
|
|
4532
|
-
float32: [-3.4e38, 3.4e38, { int: false }],
|
|
4533
|
-
float64: [-Number.MAX_VALUE, Number.MAX_VALUE, { int: false }],
|
|
4534
|
-
};
|
|
4535
5871
|
/**
|
|
4536
5872
|
* Find all named models that could have been the source of the given
|
|
4537
5873
|
* property. This includes the named parents of all property sources in a
|
|
@@ -4831,7 +6167,7 @@ function reportDeprecation(program, target, message, reportFunc) {
|
|
|
4831
6167
|
function applyDecoratorToType(program, decApp, target) {
|
|
4832
6168
|
compilerAssert("decorators" in target, "Cannot apply decorator to non-decoratable type", target);
|
|
4833
6169
|
for (const arg of decApp.args) {
|
|
4834
|
-
if (isErrorType(arg.value)) {
|
|
6170
|
+
if (isType(arg.value) && isErrorType(arg.value)) {
|
|
4835
6171
|
// If one of the decorator argument is an error don't run it.
|
|
4836
6172
|
return;
|
|
4837
6173
|
}
|
|
@@ -4886,23 +6222,6 @@ function createDecoratorContext(program, decApp) {
|
|
|
4886
6222
|
},
|
|
4887
6223
|
};
|
|
4888
6224
|
}
|
|
4889
|
-
/**
|
|
4890
|
-
* Convert TypeSpec argument to JS argument.
|
|
4891
|
-
*/
|
|
4892
|
-
function marshalArgumentsForJS(args) {
|
|
4893
|
-
return args.map((arg) => {
|
|
4894
|
-
if (arg.kind === "Boolean" || arg.kind === "String" || arg.kind === "Number") {
|
|
4895
|
-
return literalTypeToValue(arg);
|
|
4896
|
-
}
|
|
4897
|
-
else if (arg.kind === "StringTemplate") {
|
|
4898
|
-
return stringTemplateToString(arg)[0];
|
|
4899
|
-
}
|
|
4900
|
-
return arg;
|
|
4901
|
-
});
|
|
4902
|
-
}
|
|
4903
|
-
function literalTypeToValue(type) {
|
|
4904
|
-
return type.value;
|
|
4905
|
-
}
|
|
4906
6225
|
function isTemplatedNode(node) {
|
|
4907
6226
|
return "templateParameters" in node && node.templateParameters.length > 0;
|
|
4908
6227
|
}
|
|
@@ -4926,9 +6245,10 @@ const ReflectionNameToKind = {
|
|
|
4926
6245
|
const _assertReflectionNameToKind = ReflectionNameToKind;
|
|
4927
6246
|
var ResolutionKind;
|
|
4928
6247
|
(function (ResolutionKind) {
|
|
4929
|
-
ResolutionKind[ResolutionKind["
|
|
4930
|
-
ResolutionKind[ResolutionKind["
|
|
4931
|
-
ResolutionKind[ResolutionKind["
|
|
6248
|
+
ResolutionKind[ResolutionKind["Value"] = 0] = "Value";
|
|
6249
|
+
ResolutionKind[ResolutionKind["Type"] = 1] = "Type";
|
|
6250
|
+
ResolutionKind[ResolutionKind["BaseType"] = 2] = "BaseType";
|
|
6251
|
+
ResolutionKind[ResolutionKind["Constraint"] = 3] = "Constraint";
|
|
4932
6252
|
})(ResolutionKind || (ResolutionKind = {}));
|
|
4933
6253
|
class PendingResolutions {
|
|
4934
6254
|
#data = new Map();
|
|
@@ -4964,4 +6284,19 @@ const defaultSymbolResolutionOptions = {
|
|
|
4964
6284
|
resolveDecorators: false,
|
|
4965
6285
|
checkTemplateTypes: true,
|
|
4966
6286
|
};
|
|
6287
|
+
/**
|
|
6288
|
+
* Convert LEGACY for projection.
|
|
6289
|
+
* THIS IS BROKEN. Some decorators will not receive the correct type.
|
|
6290
|
+
* It has been broken since the introduction of valueof.
|
|
6291
|
+
* As projection as put on hold as long as versioning works we are in a good state.
|
|
6292
|
+
*/
|
|
6293
|
+
function unsafe_projectionArgumentMarshalForJS(arg) {
|
|
6294
|
+
if (arg.kind === "Boolean" || arg.kind === "String" || arg.kind === "Number") {
|
|
6295
|
+
return arg.value;
|
|
6296
|
+
}
|
|
6297
|
+
else if (arg.kind === "StringTemplate") {
|
|
6298
|
+
return arg.stringValue;
|
|
6299
|
+
}
|
|
6300
|
+
return arg;
|
|
6301
|
+
}
|
|
4967
6302
|
//# sourceMappingURL=checker.js.map
|