@typespec/compiler 0.60.0-dev.9 → 0.61.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/manifest.js +2 -2
- package/dist/src/config/config-loader.js +7 -7
- package/dist/src/config/config-loader.js.map +1 -1
- package/dist/src/core/checker.d.ts.map +1 -1
- package/dist/src/core/checker.js +23 -577
- package/dist/src/core/checker.js.map +1 -1
- package/dist/src/core/messages.d.ts +25 -12
- package/dist/src/core/messages.d.ts.map +1 -1
- package/dist/src/core/messages.js +7 -2
- package/dist/src/core/messages.js.map +1 -1
- package/dist/src/core/program.d.ts.map +1 -1
- package/dist/src/core/program.js +47 -187
- package/dist/src/core/program.js.map +1 -1
- package/dist/src/core/schema-validator.d.ts.map +1 -1
- package/dist/src/core/schema-validator.js +8 -18
- package/dist/src/core/schema-validator.js.map +1 -1
- package/dist/src/core/semantic-walker.d.ts.map +1 -1
- package/dist/src/core/semantic-walker.js +2 -0
- package/dist/src/core/semantic-walker.js.map +1 -1
- package/dist/src/core/source-loader.d.ts +34 -0
- package/dist/src/core/source-loader.d.ts.map +1 -0
- package/dist/src/core/source-loader.js +241 -0
- package/dist/src/core/source-loader.js.map +1 -0
- package/dist/src/core/type-relation-checker.d.ts +32 -0
- package/dist/src/core/type-relation-checker.d.ts.map +1 -0
- package/dist/src/core/type-relation-checker.js +643 -0
- package/dist/src/core/type-relation-checker.js.map +1 -0
- package/dist/src/lib/service.js +2 -2
- package/dist/src/lib/service.js.map +1 -1
- package/package.json +2 -2
- package/templates/scaffolding.json +4 -4
package/dist/src/core/checker.js
CHANGED
|
@@ -10,14 +10,13 @@ import { validateInheritanceDiscriminatedUnions } from "./helpers/discriminator-
|
|
|
10
10
|
import { getLocationContext } from "./helpers/location-context.js";
|
|
11
11
|
import { explainStringTemplateNotSerializable } from "./helpers/string-template-utils.js";
|
|
12
12
|
import { getEntityName, getNamespaceFullName, getTypeName, } from "./helpers/type-name-utils.js";
|
|
13
|
-
import { getMaxItems, getMaxLength, getMaxValueAsNumeric, getMaxValueExclusiveAsNumeric, getMinItems, getMinLength, getMinValueAsNumeric, getMinValueExclusiveAsNumeric, } from "./intrinsic-type-state.js";
|
|
14
13
|
import { canNumericConstraintBeJsNumber, legacyMarshallTypeForJS, marshallTypeForJS, } from "./js-marshaller.js";
|
|
15
14
|
import { createDiagnostic } from "./messages.js";
|
|
16
|
-
import { numericRanges } from "./numeric-ranges.js";
|
|
17
15
|
import { Numeric } from "./numeric.js";
|
|
18
16
|
import { exprIsBareIdentifier, getFirstAncestor, getIdentifierContext, hasParseError, visitChildren, } from "./parser.js";
|
|
19
17
|
import { createProjectionMembers } from "./projection-members.js";
|
|
20
|
-
import {
|
|
18
|
+
import { createTypeRelationChecker } from "./type-relation-checker.js";
|
|
19
|
+
import { getFullyQualifiedSymbolName, getParentTemplateNode, isArrayModelType, isErrorType, isNullType, isTemplateInstance, isType, isValue, } from "./type-utils.js";
|
|
21
20
|
import { IdentifierKind, SyntaxKind, } from "./types.js";
|
|
22
21
|
/**
|
|
23
22
|
* Maps type arguments to type instantiation.
|
|
@@ -122,14 +121,16 @@ export function createChecker(program) {
|
|
|
122
121
|
createFunctionType,
|
|
123
122
|
createLiteralType,
|
|
124
123
|
finishType,
|
|
125
|
-
isTypeAssignableTo,
|
|
126
124
|
isStdType,
|
|
127
125
|
getStdType,
|
|
128
126
|
resolveTypeReference,
|
|
129
127
|
getValueForNode,
|
|
130
128
|
getTypeOrValueForNode,
|
|
131
129
|
getValueExactType,
|
|
130
|
+
isTypeAssignableTo: undefined,
|
|
132
131
|
};
|
|
132
|
+
const relation = createTypeRelationChecker(program, checker);
|
|
133
|
+
checker.isTypeAssignableTo = relation.isTypeAssignableTo;
|
|
133
134
|
const projectionMembers = createProjectionMembers(checker);
|
|
134
135
|
return checker;
|
|
135
136
|
function reportCheckerDiagnostic(diagnostic) {
|
|
@@ -534,7 +535,8 @@ export function createChecker(program) {
|
|
|
534
535
|
}
|
|
535
536
|
function canTryLegacyCast(target, constraint) {
|
|
536
537
|
return Boolean(constraint?.valueType &&
|
|
537
|
-
!(constraint.type &&
|
|
538
|
+
!(constraint.type &&
|
|
539
|
+
ignoreDiagnostics(relation.isTypeAssignableTo(target, constraint.type, target))));
|
|
538
540
|
}
|
|
539
541
|
/**
|
|
540
542
|
* Gets a type or value depending on the node and current constraint.
|
|
@@ -1353,15 +1355,16 @@ export function createChecker(program) {
|
|
|
1353
1355
|
if (marshalling === "legacy") {
|
|
1354
1356
|
for (const param of decorator.parameters) {
|
|
1355
1357
|
if (param.type.valueType) {
|
|
1356
|
-
if (ignoreDiagnostics(isTypeAssignableTo(nullType, param.type.valueType, param.type))) {
|
|
1358
|
+
if (ignoreDiagnostics(relation.isTypeAssignableTo(nullType, param.type.valueType, param.type))) {
|
|
1357
1359
|
reportDeprecatedLegacyMarshalling(param, "null as a type");
|
|
1358
1360
|
}
|
|
1359
1361
|
else if (param.type.valueType.kind === "Enum" ||
|
|
1360
1362
|
param.type.valueType.kind === "EnumMember" ||
|
|
1361
|
-
(isReflectionType(param.type.valueType) &&
|
|
1363
|
+
(relation.isReflectionType(param.type.valueType) &&
|
|
1364
|
+
param.type.valueType.name === "EnumMember")) {
|
|
1362
1365
|
reportDeprecatedLegacyMarshalling(param, "enum members");
|
|
1363
1366
|
}
|
|
1364
|
-
else if (ignoreDiagnostics(isTypeAssignableTo(param.type.valueType, getStdType("numeric"), param.type.valueType)) &&
|
|
1367
|
+
else if (ignoreDiagnostics(relation.isTypeAssignableTo(param.type.valueType, getStdType("numeric"), param.type.valueType)) &&
|
|
1365
1368
|
!canNumericConstraintBeJsNumber(param.type.valueType)) {
|
|
1366
1369
|
reportDeprecatedLegacyMarshalling(param, "a numeric type that is not representable as a JS Number");
|
|
1367
1370
|
}
|
|
@@ -2918,7 +2921,7 @@ export function createChecker(program) {
|
|
|
2918
2921
|
]);
|
|
2919
2922
|
return;
|
|
2920
2923
|
}
|
|
2921
|
-
const [valid, diagnostics] = isTypeAssignableTo(property.type, indexer.value, diagnosticTarget);
|
|
2924
|
+
const [valid, diagnostics] = relation.isTypeAssignableTo(property.type, indexer.value, diagnosticTarget);
|
|
2922
2925
|
if (!valid)
|
|
2923
2926
|
reportCheckerDiagnostic(createDiagnostic({
|
|
2924
2927
|
code: "incompatible-indexer",
|
|
@@ -3092,7 +3095,7 @@ export function createChecker(program) {
|
|
|
3092
3095
|
}
|
|
3093
3096
|
switch (type.kind) {
|
|
3094
3097
|
case "Scalar":
|
|
3095
|
-
if (ignoreDiagnostics(isTypeAssignableTo(literalType, type, literalType))) {
|
|
3098
|
+
if (ignoreDiagnostics(checker.isTypeAssignableTo(literalType, type, literalType))) {
|
|
3096
3099
|
return type;
|
|
3097
3100
|
}
|
|
3098
3101
|
return undefined;
|
|
@@ -3333,13 +3336,13 @@ export function createChecker(program) {
|
|
|
3333
3336
|
if (target.kind === "ScalarConstructor") {
|
|
3334
3337
|
return createScalarValue(node, mapper, target);
|
|
3335
3338
|
}
|
|
3336
|
-
if (areScalarsRelated(target, getStdType("string"))) {
|
|
3339
|
+
if (relation.areScalarsRelated(target, getStdType("string"))) {
|
|
3337
3340
|
return checkPrimitiveArg(node, target, "StringValue");
|
|
3338
3341
|
}
|
|
3339
|
-
else if (areScalarsRelated(target, getStdType("numeric"))) {
|
|
3342
|
+
else if (relation.areScalarsRelated(target, getStdType("numeric"))) {
|
|
3340
3343
|
return checkPrimitiveArg(node, target, "NumericValue");
|
|
3341
3344
|
}
|
|
3342
|
-
else if (areScalarsRelated(target, getStdType("boolean"))) {
|
|
3345
|
+
else if (relation.areScalarsRelated(target, getStdType("boolean"))) {
|
|
3343
3346
|
return checkPrimitiveArg(node, target, "BooleanValue");
|
|
3344
3347
|
}
|
|
3345
3348
|
else {
|
|
@@ -3419,7 +3422,7 @@ export function createChecker(program) {
|
|
|
3419
3422
|
}
|
|
3420
3423
|
const overriddenProp = getOverriddenProperty(newProp);
|
|
3421
3424
|
if (overriddenProp) {
|
|
3422
|
-
const [isAssignable, _] = isTypeAssignableTo(newProp.type, overriddenProp.type, newProp);
|
|
3425
|
+
const [isAssignable, _] = relation.isTypeAssignableTo(newProp.type, overriddenProp.type, newProp);
|
|
3423
3426
|
const parentType = getTypeName(overriddenProp.type);
|
|
3424
3427
|
const newPropType = getTypeName(newProp.type);
|
|
3425
3428
|
let invalid = false;
|
|
@@ -3920,7 +3923,7 @@ export function createChecker(program) {
|
|
|
3920
3923
|
if (defaultValue === null) {
|
|
3921
3924
|
return null;
|
|
3922
3925
|
}
|
|
3923
|
-
const [related, diagnostics] = isValueOfType(defaultValue, type, defaultNode);
|
|
3926
|
+
const [related, diagnostics] = relation.isValueOfType(defaultValue, type, defaultNode);
|
|
3924
3927
|
if (!related) {
|
|
3925
3928
|
reportCheckerDiagnostics(diagnostics);
|
|
3926
3929
|
return null;
|
|
@@ -4003,7 +4006,7 @@ export function createChecker(program) {
|
|
|
4003
4006
|
}
|
|
4004
4007
|
/** Check the decorator target is valid */
|
|
4005
4008
|
function checkDecoratorTarget(targetType, declaration, decoratorNode) {
|
|
4006
|
-
const [targetValid] = isTypeAssignableTo(targetType, declaration.target.type, decoratorNode);
|
|
4009
|
+
const [targetValid] = relation.isTypeAssignableTo(targetType, declaration.target.type, decoratorNode);
|
|
4007
4010
|
if (!targetValid) {
|
|
4008
4011
|
reportCheckerDiagnostic(createDiagnostic({
|
|
4009
4012
|
code: "decorator-wrong-target",
|
|
@@ -4164,7 +4167,7 @@ export function createChecker(program) {
|
|
|
4164
4167
|
return value;
|
|
4165
4168
|
}
|
|
4166
4169
|
function checkArgumentAssignable(argumentType, parameterType, diagnosticTarget) {
|
|
4167
|
-
const [valid] = isTypeAssignableTo(argumentType, parameterType, diagnosticTarget);
|
|
4170
|
+
const [valid] = relation.isTypeAssignableTo(argumentType, parameterType, diagnosticTarget);
|
|
4168
4171
|
if (!valid) {
|
|
4169
4172
|
reportCheckerDiagnostic(createDiagnostic({
|
|
4170
4173
|
code: "invalid-argument",
|
|
@@ -5599,7 +5602,7 @@ export function createChecker(program) {
|
|
|
5599
5602
|
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5600
5603
|
*/
|
|
5601
5604
|
function checkTypeOfValueMatchConstraint(source, constraint, diagnosticTarget) {
|
|
5602
|
-
const [related, diagnostics] = isTypeAssignableTo(source, constraint.type, diagnosticTarget);
|
|
5605
|
+
const [related, diagnostics] = relation.isTypeAssignableTo(source, constraint.type, diagnosticTarget);
|
|
5603
5606
|
if (!related) {
|
|
5604
5607
|
if (constraint.kind === "argument") {
|
|
5605
5608
|
reportCheckerDiagnostic(createDiagnostic({
|
|
@@ -5624,552 +5627,19 @@ export function createChecker(program) {
|
|
|
5624
5627
|
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5625
5628
|
*/
|
|
5626
5629
|
function checkTypeAssignable(source, target, diagnosticTarget) {
|
|
5627
|
-
const [related, diagnostics] = isTypeAssignableTo(source, target, diagnosticTarget);
|
|
5630
|
+
const [related, diagnostics] = relation.isTypeAssignableTo(source, target, diagnosticTarget);
|
|
5628
5631
|
if (!related) {
|
|
5629
5632
|
reportCheckerDiagnostics(diagnostics);
|
|
5630
5633
|
}
|
|
5631
5634
|
return related;
|
|
5632
5635
|
}
|
|
5633
5636
|
function checkValueOfType(source, target, diagnosticTarget) {
|
|
5634
|
-
const [related, diagnostics] = isValueOfType(source, target, diagnosticTarget);
|
|
5637
|
+
const [related, diagnostics] = relation.isValueOfType(source, target, diagnosticTarget);
|
|
5635
5638
|
if (!related) {
|
|
5636
5639
|
reportCheckerDiagnostics(diagnostics);
|
|
5637
5640
|
}
|
|
5638
5641
|
return related;
|
|
5639
5642
|
}
|
|
5640
|
-
/**
|
|
5641
|
-
* Check if the source type can be assigned to the target type.
|
|
5642
|
-
* @param source Source type
|
|
5643
|
-
* @param target Target type
|
|
5644
|
-
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5645
|
-
*/
|
|
5646
|
-
function isTypeAssignableTo(source, target, diagnosticTarget) {
|
|
5647
|
-
const [related, diagnostics] = isTypeAssignableToInternal(source, target, diagnosticTarget, new MultiKeyMap());
|
|
5648
|
-
return [related === Related.true, diagnostics];
|
|
5649
|
-
}
|
|
5650
|
-
/**
|
|
5651
|
-
* Check if the given Value type is of the given type.
|
|
5652
|
-
* @param source Value
|
|
5653
|
-
* @param target Target type
|
|
5654
|
-
* @param diagnosticTarget Target for the diagnostic, unless something better can be inferred.
|
|
5655
|
-
*/
|
|
5656
|
-
function isValueOfType(source, target, diagnosticTarget) {
|
|
5657
|
-
const [related, diagnostics] = isValueOfTypeInternal(source, target, diagnosticTarget, new MultiKeyMap());
|
|
5658
|
-
return [related === Related.true, diagnostics];
|
|
5659
|
-
}
|
|
5660
|
-
function isTypeAssignableToInternal(source, target, diagnosticTarget, relationCache) {
|
|
5661
|
-
const cached = relationCache.get([source, target]);
|
|
5662
|
-
if (cached !== undefined) {
|
|
5663
|
-
return [cached, []];
|
|
5664
|
-
}
|
|
5665
|
-
const [result, diagnostics] = isTypeAssignableToWorker(source, target, diagnosticTarget, new MultiKeyMap());
|
|
5666
|
-
relationCache.set([source, target], result);
|
|
5667
|
-
return [result, diagnostics];
|
|
5668
|
-
}
|
|
5669
|
-
function isTypeAssignableToWorker(source, target, diagnosticTarget, relationCache) {
|
|
5670
|
-
// BACKCOMPAT: Allow certain type to be accepted as values
|
|
5671
|
-
if ("kind" in source &&
|
|
5672
|
-
"entityKind" in target &&
|
|
5673
|
-
source.kind === "TemplateParameter" &&
|
|
5674
|
-
source.constraint?.type &&
|
|
5675
|
-
source.constraint.valueType === undefined &&
|
|
5676
|
-
target.entityKind === "MixedParameterConstraint" &&
|
|
5677
|
-
target.valueType) {
|
|
5678
|
-
const [assignable] = isTypeAssignableToInternal(source.constraint.type, target.valueType, diagnosticTarget, relationCache);
|
|
5679
|
-
if (assignable) {
|
|
5680
|
-
const constraint = getEntityName(source.constraint);
|
|
5681
|
-
reportDeprecated(program, `Template constrainted to '${constraint}' will not be assignable to '${getEntityName(target)}' in the future. Update the constraint to be 'valueof ${constraint}'`, diagnosticTarget);
|
|
5682
|
-
return [Related.true, []];
|
|
5683
|
-
}
|
|
5684
|
-
}
|
|
5685
|
-
if ("kind" in source && source.kind === "TemplateParameter") {
|
|
5686
|
-
source = source.constraint ?? unknownType;
|
|
5687
|
-
}
|
|
5688
|
-
if (target.entityKind === "Indeterminate") {
|
|
5689
|
-
target = target.type;
|
|
5690
|
-
}
|
|
5691
|
-
if (source === target)
|
|
5692
|
-
return [Related.true, []];
|
|
5693
|
-
if (isValue(target)) {
|
|
5694
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5695
|
-
}
|
|
5696
|
-
if (source.entityKind === "Indeterminate") {
|
|
5697
|
-
return isIndeterminateEntityAssignableTo(source, target, diagnosticTarget, relationCache);
|
|
5698
|
-
}
|
|
5699
|
-
if (target.entityKind === "MixedParameterConstraint") {
|
|
5700
|
-
return isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache);
|
|
5701
|
-
}
|
|
5702
|
-
if (isValue(source) || (source.entityKind === "MixedParameterConstraint" && source.valueType)) {
|
|
5703
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5704
|
-
}
|
|
5705
|
-
if (source.entityKind === "MixedParameterConstraint") {
|
|
5706
|
-
return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
|
|
5707
|
-
}
|
|
5708
|
-
const isSimpleTypeRelated = isSimpleTypeAssignableTo(source, target);
|
|
5709
|
-
if (isSimpleTypeRelated === true) {
|
|
5710
|
-
return [Related.true, []];
|
|
5711
|
-
}
|
|
5712
|
-
else if (isSimpleTypeRelated === false) {
|
|
5713
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5714
|
-
}
|
|
5715
|
-
if (source.kind === "Union") {
|
|
5716
|
-
for (const variant of source.variants.values()) {
|
|
5717
|
-
const [variantAssignable] = isTypeAssignableToInternal(variant.type, target, diagnosticTarget, relationCache);
|
|
5718
|
-
if (!variantAssignable) {
|
|
5719
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5720
|
-
}
|
|
5721
|
-
}
|
|
5722
|
-
return [Related.true, []];
|
|
5723
|
-
}
|
|
5724
|
-
if (target.kind === "Model" &&
|
|
5725
|
-
source.kind === "Model" &&
|
|
5726
|
-
target.name !== "object" &&
|
|
5727
|
-
target.indexer === undefined &&
|
|
5728
|
-
source.indexer &&
|
|
5729
|
-
source.indexer.key.name === "integer") {
|
|
5730
|
-
return [
|
|
5731
|
-
Related.false,
|
|
5732
|
-
[
|
|
5733
|
-
createDiagnostic({
|
|
5734
|
-
code: "missing-index",
|
|
5735
|
-
format: {
|
|
5736
|
-
indexType: getTypeName(source.indexer.key),
|
|
5737
|
-
sourceType: getTypeName(target),
|
|
5738
|
-
},
|
|
5739
|
-
target: diagnosticTarget,
|
|
5740
|
-
}),
|
|
5741
|
-
],
|
|
5742
|
-
];
|
|
5743
|
-
}
|
|
5744
|
-
else if (target.kind === "Model" &&
|
|
5745
|
-
isArrayModelType(program, target) &&
|
|
5746
|
-
source.kind === "Model") {
|
|
5747
|
-
if (isArrayModelType(program, source)) {
|
|
5748
|
-
return hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache);
|
|
5749
|
-
}
|
|
5750
|
-
else {
|
|
5751
|
-
// For other models just fallback to unassignable
|
|
5752
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5753
|
-
}
|
|
5754
|
-
}
|
|
5755
|
-
else if (target.kind === "Model" && source.kind === "Model") {
|
|
5756
|
-
return isModelRelatedTo(source, target, diagnosticTarget, relationCache);
|
|
5757
|
-
}
|
|
5758
|
-
else if (target.kind === "Model" &&
|
|
5759
|
-
isArrayModelType(program, target) &&
|
|
5760
|
-
source.kind === "Tuple") {
|
|
5761
|
-
return isTupleAssignableToArray(source, target, diagnosticTarget, relationCache);
|
|
5762
|
-
}
|
|
5763
|
-
else if (target.kind === "Tuple" && source.kind === "Tuple") {
|
|
5764
|
-
return isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache);
|
|
5765
|
-
}
|
|
5766
|
-
else if (target.kind === "Union") {
|
|
5767
|
-
return isAssignableToUnion(source, target, diagnosticTarget, relationCache);
|
|
5768
|
-
}
|
|
5769
|
-
else if (target.kind === "Enum") {
|
|
5770
|
-
return isAssignableToEnum(source, target, diagnosticTarget);
|
|
5771
|
-
}
|
|
5772
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5773
|
-
}
|
|
5774
|
-
function isIndeterminateEntityAssignableTo(indeterminate, target, diagnosticTarget, relationCache) {
|
|
5775
|
-
const [typeRelated, typeDiagnostics] = isTypeAssignableToInternal(indeterminate.type, target, diagnosticTarget, relationCache);
|
|
5776
|
-
if (typeRelated) {
|
|
5777
|
-
return [Related.true, []];
|
|
5778
|
-
}
|
|
5779
|
-
if (target.entityKind === "MixedParameterConstraint" && target.valueType) {
|
|
5780
|
-
const [valueRelated] = isTypeAssignableToInternal(indeterminate.type, target.valueType, diagnosticTarget, relationCache);
|
|
5781
|
-
if (valueRelated) {
|
|
5782
|
-
return [Related.true, []];
|
|
5783
|
-
}
|
|
5784
|
-
}
|
|
5785
|
-
return [Related.false, typeDiagnostics];
|
|
5786
|
-
}
|
|
5787
|
-
function isAssignableToValueType(source, target, diagnosticTarget, relationCache) {
|
|
5788
|
-
if (!isValue(source)) {
|
|
5789
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5790
|
-
}
|
|
5791
|
-
return isValueOfTypeInternal(source, target, diagnosticTarget, relationCache);
|
|
5792
|
-
}
|
|
5793
|
-
function isAssignableToMixedParameterConstraint(source, target, diagnosticTarget, relationCache) {
|
|
5794
|
-
if ("entityKind" in source && source.entityKind === "MixedParameterConstraint") {
|
|
5795
|
-
if (source.type && target.type) {
|
|
5796
|
-
const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.type, target.type, diagnosticTarget, relationCache);
|
|
5797
|
-
if (variantAssignable === Related.false) {
|
|
5798
|
-
return [Related.false, diagnostics];
|
|
5799
|
-
}
|
|
5800
|
-
return [Related.true, []];
|
|
5801
|
-
}
|
|
5802
|
-
if (source.valueType && target.valueType) {
|
|
5803
|
-
const [variantAssignable, diagnostics] = isTypeAssignableToInternal(source.valueType, target.valueType, diagnosticTarget, relationCache);
|
|
5804
|
-
if (variantAssignable === Related.false) {
|
|
5805
|
-
return [Related.false, diagnostics];
|
|
5806
|
-
}
|
|
5807
|
-
return [Related.true, []];
|
|
5808
|
-
}
|
|
5809
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5810
|
-
}
|
|
5811
|
-
if (target.type) {
|
|
5812
|
-
const [related] = isTypeAssignableToInternal(source, target.type, diagnosticTarget, relationCache);
|
|
5813
|
-
if (related) {
|
|
5814
|
-
return [Related.true, []];
|
|
5815
|
-
}
|
|
5816
|
-
}
|
|
5817
|
-
if (target.valueType) {
|
|
5818
|
-
const [related] = isAssignableToValueType(source, target.valueType, diagnosticTarget, relationCache);
|
|
5819
|
-
if (related) {
|
|
5820
|
-
return [Related.true, []];
|
|
5821
|
-
}
|
|
5822
|
-
}
|
|
5823
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
5824
|
-
}
|
|
5825
|
-
/** Check if the value is assignable to the given type. */
|
|
5826
|
-
function isValueOfTypeInternal(source, target, diagnosticTarget, relationCache) {
|
|
5827
|
-
return isTypeAssignableToInternal(source.type, target, diagnosticTarget, relationCache);
|
|
5828
|
-
}
|
|
5829
|
-
function isReflectionType(type) {
|
|
5830
|
-
return (type.kind === "Model" &&
|
|
5831
|
-
type.namespace?.name === "Reflection" &&
|
|
5832
|
-
type.namespace?.namespace?.name === "TypeSpec");
|
|
5833
|
-
}
|
|
5834
|
-
function isRelatedToScalar(source, target) {
|
|
5835
|
-
switch (source.kind) {
|
|
5836
|
-
case "Number":
|
|
5837
|
-
return isNumericLiteralRelatedTo(source, target);
|
|
5838
|
-
case "String":
|
|
5839
|
-
case "StringTemplate":
|
|
5840
|
-
return isStringLiteralRelatedTo(source, target);
|
|
5841
|
-
case "Boolean":
|
|
5842
|
-
return areScalarsRelated(target, getStdType("boolean"));
|
|
5843
|
-
case "Scalar":
|
|
5844
|
-
return areScalarsRelated(source, target);
|
|
5845
|
-
case "Union":
|
|
5846
|
-
return undefined;
|
|
5847
|
-
default:
|
|
5848
|
-
return false;
|
|
5849
|
-
}
|
|
5850
|
-
}
|
|
5851
|
-
function areScalarsRelated(source, target) {
|
|
5852
|
-
let current = source;
|
|
5853
|
-
while (current) {
|
|
5854
|
-
if (current === target) {
|
|
5855
|
-
return true;
|
|
5856
|
-
}
|
|
5857
|
-
current = current.baseScalar;
|
|
5858
|
-
}
|
|
5859
|
-
return false;
|
|
5860
|
-
}
|
|
5861
|
-
function isSimpleTypeAssignableTo(source, target) {
|
|
5862
|
-
if (isNeverType(source))
|
|
5863
|
-
return true;
|
|
5864
|
-
if (isVoidType(target))
|
|
5865
|
-
return false;
|
|
5866
|
-
if (isUnknownType(target))
|
|
5867
|
-
return true;
|
|
5868
|
-
if (isReflectionType(target)) {
|
|
5869
|
-
return source.kind === ReflectionNameToKind[target.name];
|
|
5870
|
-
}
|
|
5871
|
-
if (target.kind === "Scalar") {
|
|
5872
|
-
return isRelatedToScalar(source, target);
|
|
5873
|
-
}
|
|
5874
|
-
if (source.kind === "Scalar" && target.kind === "Model") {
|
|
5875
|
-
return false;
|
|
5876
|
-
}
|
|
5877
|
-
if (target.kind === "String") {
|
|
5878
|
-
return ((source.kind === "String" && source.value === target.value) ||
|
|
5879
|
-
(source.kind === "StringTemplate" && source.stringValue === target.value));
|
|
5880
|
-
}
|
|
5881
|
-
if (target.kind === "StringTemplate" && target.stringValue) {
|
|
5882
|
-
return ((source.kind === "String" && source.value === target.stringValue) ||
|
|
5883
|
-
(source.kind === "StringTemplate" && source.stringValue === target.stringValue));
|
|
5884
|
-
}
|
|
5885
|
-
if (target.kind === "Number") {
|
|
5886
|
-
return source.kind === "Number" && target.value === source.value;
|
|
5887
|
-
}
|
|
5888
|
-
return undefined;
|
|
5889
|
-
}
|
|
5890
|
-
function isNumericLiteralRelatedTo(source, target) {
|
|
5891
|
-
// First check that the source numeric literal is assignable to the target scalar
|
|
5892
|
-
if (!isNumericAssignableToNumericScalar(source.numericValue, target)) {
|
|
5893
|
-
return false;
|
|
5894
|
-
}
|
|
5895
|
-
const min = getMinValueAsNumeric(program, target);
|
|
5896
|
-
const max = getMaxValueAsNumeric(program, target);
|
|
5897
|
-
const minExclusive = getMinValueExclusiveAsNumeric(program, target);
|
|
5898
|
-
const maxExclusive = getMaxValueExclusiveAsNumeric(program, target);
|
|
5899
|
-
if (min && source.numericValue.lt(min)) {
|
|
5900
|
-
return false;
|
|
5901
|
-
}
|
|
5902
|
-
if (minExclusive && source.numericValue.lte(minExclusive)) {
|
|
5903
|
-
return false;
|
|
5904
|
-
}
|
|
5905
|
-
if (max && source.numericValue.gt(max)) {
|
|
5906
|
-
return false;
|
|
5907
|
-
}
|
|
5908
|
-
if (maxExclusive && source.numericValue.gte(maxExclusive)) {
|
|
5909
|
-
return false;
|
|
5910
|
-
}
|
|
5911
|
-
return true;
|
|
5912
|
-
}
|
|
5913
|
-
function isNumericAssignableToNumericScalar(source, target) {
|
|
5914
|
-
// if the target does not derive from numeric, then it can't be assigned a numeric literal
|
|
5915
|
-
if (!areScalarsRelated(target.projectionBase ?? target, getStdType("numeric"))) {
|
|
5916
|
-
return false;
|
|
5917
|
-
}
|
|
5918
|
-
// With respect to literal assignability a custom numeric scalar is
|
|
5919
|
-
// equivalent to its nearest TypeSpec.* base. Adjust target accordingly.
|
|
5920
|
-
while (!target.namespace || !isTypeSpecNamespace(target.namespace)) {
|
|
5921
|
-
compilerAssert(target.baseScalar, "Should not be possible to be derived from TypeSpec.numeric and not have a base when not in TypeSpec namespace.");
|
|
5922
|
-
target = target.baseScalar;
|
|
5923
|
-
}
|
|
5924
|
-
if (target.name === "numeric")
|
|
5925
|
-
return true;
|
|
5926
|
-
if (target.name === "decimal")
|
|
5927
|
-
return true;
|
|
5928
|
-
if (target.name === "decimal128")
|
|
5929
|
-
return true;
|
|
5930
|
-
const isInt = source.isInteger;
|
|
5931
|
-
if (target.name === "integer")
|
|
5932
|
-
return isInt;
|
|
5933
|
-
if (target.name === "float")
|
|
5934
|
-
return true;
|
|
5935
|
-
if (!(target.name in numericRanges))
|
|
5936
|
-
return false;
|
|
5937
|
-
const [low, high, options] = numericRanges[target.name];
|
|
5938
|
-
return source.gte(low) && source.lte(high) && (!options.int || isInt);
|
|
5939
|
-
}
|
|
5940
|
-
function isStringLiteralRelatedTo(source, target) {
|
|
5941
|
-
if (!areScalarsRelated(target.projectionBase ?? target, getStdType("string"))) {
|
|
5942
|
-
return false;
|
|
5943
|
-
}
|
|
5944
|
-
if (source.kind === "StringTemplate") {
|
|
5945
|
-
return true;
|
|
5946
|
-
}
|
|
5947
|
-
const len = source.value.length;
|
|
5948
|
-
const min = getMinLength(program, target);
|
|
5949
|
-
const max = getMaxLength(program, target);
|
|
5950
|
-
if (min && len < min) {
|
|
5951
|
-
return false;
|
|
5952
|
-
}
|
|
5953
|
-
if (max && len > max) {
|
|
5954
|
-
return false;
|
|
5955
|
-
}
|
|
5956
|
-
return true;
|
|
5957
|
-
}
|
|
5958
|
-
function isModelRelatedTo(source, target, diagnosticTarget, relationCache) {
|
|
5959
|
-
relationCache.set([source, target], Related.maybe);
|
|
5960
|
-
const diagnostics = [];
|
|
5961
|
-
const remainingProperties = new Map(source.properties);
|
|
5962
|
-
for (const prop of walkPropertiesInherited(target)) {
|
|
5963
|
-
const sourceProperty = getProperty(source, prop.name);
|
|
5964
|
-
if (sourceProperty === undefined) {
|
|
5965
|
-
if (!prop.optional) {
|
|
5966
|
-
diagnostics.push(createDiagnostic({
|
|
5967
|
-
code: "missing-property",
|
|
5968
|
-
format: {
|
|
5969
|
-
propertyName: prop.name,
|
|
5970
|
-
sourceType: getTypeName(source),
|
|
5971
|
-
targetType: getTypeName(target),
|
|
5972
|
-
},
|
|
5973
|
-
target: source,
|
|
5974
|
-
}));
|
|
5975
|
-
}
|
|
5976
|
-
}
|
|
5977
|
-
else {
|
|
5978
|
-
remainingProperties.delete(prop.name);
|
|
5979
|
-
if (sourceProperty.optional && !prop.optional) {
|
|
5980
|
-
diagnostics.push(createDiagnostic({
|
|
5981
|
-
code: "property-required",
|
|
5982
|
-
format: {
|
|
5983
|
-
propName: prop.name,
|
|
5984
|
-
targetType: getTypeName(target),
|
|
5985
|
-
},
|
|
5986
|
-
target: diagnosticTarget,
|
|
5987
|
-
}));
|
|
5988
|
-
}
|
|
5989
|
-
const [related, propDiagnostics] = isTypeAssignableToInternal(sourceProperty.type, prop.type, diagnosticTarget, relationCache);
|
|
5990
|
-
if (!related) {
|
|
5991
|
-
diagnostics.push(...propDiagnostics);
|
|
5992
|
-
}
|
|
5993
|
-
}
|
|
5994
|
-
}
|
|
5995
|
-
if (target.indexer) {
|
|
5996
|
-
const [_, indexerDiagnostics] = arePropertiesAssignableToIndexer(remainingProperties, target.indexer.value, diagnosticTarget, relationCache);
|
|
5997
|
-
diagnostics.push(...indexerDiagnostics);
|
|
5998
|
-
// For anonymous models we don't need an indexer
|
|
5999
|
-
if (source.name !== "" && target.indexer.key.name !== "integer") {
|
|
6000
|
-
const [related, indexDiagnostics] = hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache);
|
|
6001
|
-
if (!related) {
|
|
6002
|
-
diagnostics.push(...indexDiagnostics);
|
|
6003
|
-
}
|
|
6004
|
-
}
|
|
6005
|
-
}
|
|
6006
|
-
else if (shouldCheckExcessProperties(source)) {
|
|
6007
|
-
for (const [propName, prop] of remainingProperties) {
|
|
6008
|
-
if (shouldCheckExcessProperty(prop)) {
|
|
6009
|
-
diagnostics.push(createDiagnostic({
|
|
6010
|
-
code: "unexpected-property",
|
|
6011
|
-
format: {
|
|
6012
|
-
propertyName: propName,
|
|
6013
|
-
type: getEntityName(target),
|
|
6014
|
-
},
|
|
6015
|
-
target: prop,
|
|
6016
|
-
}));
|
|
6017
|
-
}
|
|
6018
|
-
}
|
|
6019
|
-
}
|
|
6020
|
-
return [diagnostics.length === 0 ? Related.true : Related.false, diagnostics];
|
|
6021
|
-
}
|
|
6022
|
-
/** If we should check for excess properties on the given model. */
|
|
6023
|
-
function shouldCheckExcessProperties(model) {
|
|
6024
|
-
return model.node?.kind === SyntaxKind.ObjectLiteral;
|
|
6025
|
-
}
|
|
6026
|
-
/** If we should check for this specific property */
|
|
6027
|
-
function shouldCheckExcessProperty(prop) {
|
|
6028
|
-
return (prop.node?.kind === SyntaxKind.ObjectLiteralProperty && prop.node.parent === prop.model?.node);
|
|
6029
|
-
}
|
|
6030
|
-
function getProperty(model, name) {
|
|
6031
|
-
return (model.properties.get(name) ??
|
|
6032
|
-
(model.baseModel !== undefined ? getProperty(model.baseModel, name) : undefined));
|
|
6033
|
-
}
|
|
6034
|
-
function arePropertiesAssignableToIndexer(properties, indexerConstaint, diagnosticTarget, relationCache) {
|
|
6035
|
-
for (const prop of properties.values()) {
|
|
6036
|
-
const [related, diagnostics] = isTypeAssignableToInternal(prop.type, indexerConstaint, diagnosticTarget, relationCache);
|
|
6037
|
-
if (!related) {
|
|
6038
|
-
return [Related.false, diagnostics];
|
|
6039
|
-
}
|
|
6040
|
-
}
|
|
6041
|
-
return [Related.true, []];
|
|
6042
|
-
}
|
|
6043
|
-
/** Check that the source model has an index, the index key match and the value of the source index is assignable to the target index. */
|
|
6044
|
-
function hasIndexAndIsAssignableTo(source, target, diagnosticTarget, relationCache) {
|
|
6045
|
-
if (source.indexer === undefined || source.indexer.key !== target.indexer.key) {
|
|
6046
|
-
return [
|
|
6047
|
-
Related.false,
|
|
6048
|
-
[
|
|
6049
|
-
createDiagnostic({
|
|
6050
|
-
code: "missing-index",
|
|
6051
|
-
format: {
|
|
6052
|
-
indexType: getTypeName(target.indexer.key),
|
|
6053
|
-
sourceType: getTypeName(source),
|
|
6054
|
-
},
|
|
6055
|
-
target: diagnosticTarget,
|
|
6056
|
-
}),
|
|
6057
|
-
],
|
|
6058
|
-
];
|
|
6059
|
-
}
|
|
6060
|
-
return isTypeAssignableToInternal(source.indexer.value, target.indexer.value, diagnosticTarget, relationCache);
|
|
6061
|
-
}
|
|
6062
|
-
function isTupleAssignableToArray(source, target, diagnosticTarget, relationCache) {
|
|
6063
|
-
const minItems = getMinItems(program, target);
|
|
6064
|
-
const maxItems = getMaxItems(program, target);
|
|
6065
|
-
if (minItems !== undefined && source.values.length < minItems) {
|
|
6066
|
-
return [
|
|
6067
|
-
Related.false,
|
|
6068
|
-
[
|
|
6069
|
-
createDiagnostic({
|
|
6070
|
-
code: "unassignable",
|
|
6071
|
-
messageId: "withDetails",
|
|
6072
|
-
format: {
|
|
6073
|
-
sourceType: getEntityName(source),
|
|
6074
|
-
targetType: getTypeName(target),
|
|
6075
|
-
details: `Source has ${source.values.length} element(s) but target requires ${minItems}.`,
|
|
6076
|
-
},
|
|
6077
|
-
target: diagnosticTarget,
|
|
6078
|
-
}),
|
|
6079
|
-
],
|
|
6080
|
-
];
|
|
6081
|
-
}
|
|
6082
|
-
if (maxItems !== undefined && source.values.length > maxItems) {
|
|
6083
|
-
return [
|
|
6084
|
-
Related.false,
|
|
6085
|
-
[
|
|
6086
|
-
createDiagnostic({
|
|
6087
|
-
code: "unassignable",
|
|
6088
|
-
messageId: "withDetails",
|
|
6089
|
-
format: {
|
|
6090
|
-
sourceType: getEntityName(source),
|
|
6091
|
-
targetType: getTypeName(target),
|
|
6092
|
-
details: `Source has ${source.values.length} element(s) but target only allows ${maxItems}.`,
|
|
6093
|
-
},
|
|
6094
|
-
target: diagnosticTarget,
|
|
6095
|
-
}),
|
|
6096
|
-
],
|
|
6097
|
-
];
|
|
6098
|
-
}
|
|
6099
|
-
for (const item of source.values) {
|
|
6100
|
-
const [related, diagnostics] = isTypeAssignableToInternal(item, target.indexer.value, diagnosticTarget, relationCache);
|
|
6101
|
-
if (!related) {
|
|
6102
|
-
return [Related.false, diagnostics];
|
|
6103
|
-
}
|
|
6104
|
-
}
|
|
6105
|
-
return [Related.true, []];
|
|
6106
|
-
}
|
|
6107
|
-
function isTupleAssignableToTuple(source, target, diagnosticTarget, relationCache) {
|
|
6108
|
-
if (source.values.length !== target.values.length) {
|
|
6109
|
-
return [
|
|
6110
|
-
Related.false,
|
|
6111
|
-
[
|
|
6112
|
-
createDiagnostic({
|
|
6113
|
-
code: "unassignable",
|
|
6114
|
-
messageId: "withDetails",
|
|
6115
|
-
format: {
|
|
6116
|
-
sourceType: getEntityName(source),
|
|
6117
|
-
targetType: getTypeName(target),
|
|
6118
|
-
details: `Source has ${source.values.length} element(s) but target requires ${target.values.length}.`,
|
|
6119
|
-
},
|
|
6120
|
-
target: diagnosticTarget,
|
|
6121
|
-
}),
|
|
6122
|
-
],
|
|
6123
|
-
];
|
|
6124
|
-
}
|
|
6125
|
-
for (const [index, sourceItem] of source.values.entries()) {
|
|
6126
|
-
const targetItem = target.values[index];
|
|
6127
|
-
const [related, diagnostics] = isTypeAssignableToInternal(sourceItem, targetItem, diagnosticTarget, relationCache);
|
|
6128
|
-
if (!related) {
|
|
6129
|
-
return [Related.false, diagnostics];
|
|
6130
|
-
}
|
|
6131
|
-
}
|
|
6132
|
-
return [Related.true, []];
|
|
6133
|
-
}
|
|
6134
|
-
function isAssignableToUnion(source, target, diagnosticTarget, relationCache) {
|
|
6135
|
-
if (source.kind === "UnionVariant" && source.union === target) {
|
|
6136
|
-
return [Related.true, []];
|
|
6137
|
-
}
|
|
6138
|
-
for (const option of target.variants.values()) {
|
|
6139
|
-
const [related] = isTypeAssignableToInternal(source, option.type, diagnosticTarget, relationCache);
|
|
6140
|
-
if (related) {
|
|
6141
|
-
return [Related.true, []];
|
|
6142
|
-
}
|
|
6143
|
-
}
|
|
6144
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
6145
|
-
}
|
|
6146
|
-
function isAssignableToEnum(source, target, diagnosticTarget) {
|
|
6147
|
-
switch (source.kind) {
|
|
6148
|
-
case "Enum":
|
|
6149
|
-
if (source === target) {
|
|
6150
|
-
return [Related.true, []];
|
|
6151
|
-
}
|
|
6152
|
-
else {
|
|
6153
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
6154
|
-
}
|
|
6155
|
-
case "EnumMember":
|
|
6156
|
-
if (source.enum === target) {
|
|
6157
|
-
return [Related.true, []];
|
|
6158
|
-
}
|
|
6159
|
-
else {
|
|
6160
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
6161
|
-
}
|
|
6162
|
-
default:
|
|
6163
|
-
return [Related.false, [createUnassignableDiagnostic(source, target, diagnosticTarget)]];
|
|
6164
|
-
}
|
|
6165
|
-
}
|
|
6166
|
-
function createUnassignableDiagnostic(source, target, diagnosticTarget) {
|
|
6167
|
-
return createDiagnostic({
|
|
6168
|
-
code: "unassignable",
|
|
6169
|
-
format: { targetType: getEntityName(target), value: getEntityName(source) },
|
|
6170
|
-
target: diagnosticTarget,
|
|
6171
|
-
});
|
|
6172
|
-
}
|
|
6173
5643
|
function isStdType(type, stdType) {
|
|
6174
5644
|
type = type.projectionBase ?? type;
|
|
6175
5645
|
if ((type.kind !== "Model" && type.kind !== "Scalar") ||
|
|
@@ -6565,24 +6035,6 @@ function createDecoratorContext(program, decApp) {
|
|
|
6565
6035
|
function isTemplatedNode(node) {
|
|
6566
6036
|
return "templateParameters" in node && node.templateParameters.length > 0;
|
|
6567
6037
|
}
|
|
6568
|
-
/**
|
|
6569
|
-
* Mapping from the reflection models to Type["kind"] value
|
|
6570
|
-
*/
|
|
6571
|
-
const ReflectionNameToKind = {
|
|
6572
|
-
Enum: "Enum",
|
|
6573
|
-
EnumMember: "EnumMember",
|
|
6574
|
-
Interface: "Interface",
|
|
6575
|
-
Model: "Model",
|
|
6576
|
-
ModelProperty: "ModelProperty",
|
|
6577
|
-
Namespace: "Namespace",
|
|
6578
|
-
Operation: "Operation",
|
|
6579
|
-
Scalar: "Scalar",
|
|
6580
|
-
TemplateParameter: "TemplateParameter",
|
|
6581
|
-
Tuple: "Tuple",
|
|
6582
|
-
Union: "Union",
|
|
6583
|
-
UnionVariant: "UnionVariant",
|
|
6584
|
-
};
|
|
6585
|
-
const _assertReflectionNameToKind = ReflectionNameToKind;
|
|
6586
6038
|
var ResolutionKind;
|
|
6587
6039
|
(function (ResolutionKind) {
|
|
6588
6040
|
ResolutionKind[ResolutionKind["Value"] = 0] = "Value";
|
|
@@ -6614,12 +6066,6 @@ class PendingResolutions {
|
|
|
6614
6066
|
}
|
|
6615
6067
|
}
|
|
6616
6068
|
}
|
|
6617
|
-
var Related;
|
|
6618
|
-
(function (Related) {
|
|
6619
|
-
Related[Related["false"] = 0] = "false";
|
|
6620
|
-
Related[Related["true"] = 1] = "true";
|
|
6621
|
-
Related[Related["maybe"] = 2] = "maybe";
|
|
6622
|
-
})(Related || (Related = {}));
|
|
6623
6069
|
const defaultSymbolResolutionOptions = {
|
|
6624
6070
|
resolveDecorators: false,
|
|
6625
6071
|
checkTemplateTypes: true,
|