jsii 5.8.22-dev.8 → 5.8.22

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.
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAllowedCovariantSubtype = isAllowedCovariantSubtype;
4
+ const spec = require("@jsii/spec");
5
+ const deepEqual = require("fast-deep-equal");
6
+ /**
7
+ * Check if subType is an allowed covariant subtype to superType
8
+ *
9
+ * This is not a generic check for subtypes or covariance, but a specific implementation
10
+ * that checks the currently allowed conditions for class covariance.
11
+ * In practice, this is driven by C# limitations.
12
+ */
13
+ function isAllowedCovariantSubtype(subType, superType, dereference) {
14
+ // one void, while other isn't => not covariant
15
+ if ((subType === undefined) !== (superType === undefined)) {
16
+ return false;
17
+ }
18
+ // Same type is always covariant
19
+ if (deepEqual(subType, superType)) {
20
+ return true;
21
+ }
22
+ // Handle array collections (covariant)
23
+ if (spec.isCollectionTypeReference(subType) && spec.isCollectionTypeReference(superType)) {
24
+ if (subType.collection.kind === 'array' && superType.collection.kind === 'array') {
25
+ return isAllowedCovariantSubtype(subType.collection.elementtype, superType.collection.elementtype, dereference);
26
+ }
27
+ // Maps are not allowed to be covariant in C#, so we exclude them here.
28
+ // This seems to be because we use C# Dictionary to implements Maps, which are using generics and generics are not allowed to be covariant
29
+ return false;
30
+ }
31
+ // Union types are currently not allowed, because we have not seen the need for it.
32
+ // Technically narrowing (removing `| Type` or subtyping) could be allowed and this works in C#.
33
+ if (spec.isUnionTypeReference(subType) || spec.isUnionTypeReference(superType)) {
34
+ return false;
35
+ }
36
+ // Intersection types are invalid, because intersections are only allowed as inputs
37
+ // and covariance is only allowed in outputs.
38
+ if (spec.isIntersectionTypeReference(subType) || spec.isIntersectionTypeReference(superType)) {
39
+ return false;
40
+ }
41
+ // Primitives can never be covariant to each other in C#
42
+ if (spec.isPrimitiveTypeReference(subType) || spec.isPrimitiveTypeReference(superType)) {
43
+ return false;
44
+ }
45
+ // We really only support covariance for named types (and lists of named types).
46
+ // To be safe, let's guard against any unknown cases.
47
+ if (!spec.isNamedTypeReference(subType) || !spec.isNamedTypeReference(superType)) {
48
+ return false;
49
+ }
50
+ const subTypeSpec = dereference(subType.fqn);
51
+ const superTypeSpec = dereference(superType.fqn);
52
+ if (!subTypeSpec || !superTypeSpec) {
53
+ return false;
54
+ }
55
+ // Handle class-to-class inheritance
56
+ if (spec.isClassType(subTypeSpec) && spec.isClassType(superTypeSpec)) {
57
+ return _classExtendsClass(subTypeSpec, superType.fqn);
58
+ }
59
+ // Handle interface-to-interface inheritance
60
+ if (spec.isInterfaceType(subTypeSpec) && spec.isInterfaceType(superTypeSpec)) {
61
+ return _interfaceExtendsInterface(subTypeSpec, superType.fqn);
62
+ }
63
+ // Handle class implementing interface
64
+ if (spec.isClassType(subTypeSpec) && spec.isInterfaceType(superTypeSpec)) {
65
+ return _classImplementsInterface(subTypeSpec, superType.fqn);
66
+ }
67
+ return false;
68
+ function _classExtendsClass(classType, targetFqn) {
69
+ let current = classType;
70
+ while (current.base) {
71
+ if (current.base === targetFqn) {
72
+ return true;
73
+ }
74
+ const baseType = dereference(current.base);
75
+ if (!spec.isClassType(baseType)) {
76
+ break;
77
+ }
78
+ current = baseType;
79
+ }
80
+ return false;
81
+ }
82
+ function _classImplementsInterface(classType, interfaceFqn) {
83
+ // Check direct interfaces
84
+ if (classType.interfaces?.includes(interfaceFqn)) {
85
+ return true;
86
+ }
87
+ // Check inherited interfaces
88
+ if (classType.interfaces) {
89
+ for (const iface of classType.interfaces) {
90
+ const ifaceType = dereference(iface);
91
+ if (spec.isInterfaceType(ifaceType) && _interfaceExtendsInterface(ifaceType, interfaceFqn)) {
92
+ return true;
93
+ }
94
+ }
95
+ }
96
+ // Check base class interfaces
97
+ if (classType.base) {
98
+ const baseType = dereference(classType.base);
99
+ if (spec.isClassType(baseType)) {
100
+ return _classImplementsInterface(baseType, interfaceFqn);
101
+ }
102
+ }
103
+ return false;
104
+ }
105
+ function _interfaceExtendsInterface(interfaceType, targetFqn) {
106
+ if (interfaceType.fqn === targetFqn) {
107
+ return true;
108
+ }
109
+ if (interfaceType.interfaces) {
110
+ for (const iface of interfaceType.interfaces) {
111
+ if (iface === targetFqn) {
112
+ return true;
113
+ }
114
+ const ifaceType = dereference(iface);
115
+ if (spec.isInterfaceType(ifaceType) && _interfaceExtendsInterface(ifaceType, targetFqn)) {
116
+ return true;
117
+ }
118
+ }
119
+ }
120
+ return false;
121
+ }
122
+ }
123
+ //# sourceMappingURL=type-analysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-analysis.js","sourceRoot":"","sources":["../src/type-analysis.ts"],"names":[],"mappings":"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","sourcesContent":["import * as spec from '@jsii/spec';\nimport * as deepEqual from 'fast-deep-equal';\nimport { TypeResolver } from './type-reference';\n\n/**\n * Check if subType is an allowed covariant subtype to superType\n *\n * This is not a generic check for subtypes or covariance, but a specific implementation\n * that checks the currently allowed conditions for class covariance.\n * In practice, this is driven by C# limitations.\n */\nexport function isAllowedCovariantSubtype(\n subType: spec.TypeReference | undefined,\n superType: spec.TypeReference | undefined,\n dereference: TypeResolver,\n): boolean {\n // one void, while other isn't => not covariant\n if ((subType === undefined) !== (superType === undefined)) {\n return false;\n }\n\n // Same type is always covariant\n if (deepEqual(subType, superType)) {\n return true;\n }\n\n // Handle array collections (covariant)\n if (spec.isCollectionTypeReference(subType) && spec.isCollectionTypeReference(superType)) {\n if (subType.collection.kind === 'array' && superType.collection.kind === 'array') {\n return isAllowedCovariantSubtype(subType.collection.elementtype, superType.collection.elementtype, dereference);\n }\n // Maps are not allowed to be covariant in C#, so we exclude them here.\n // This seems to be because we use C# Dictionary to implements Maps, which are using generics and generics are not allowed to be covariant\n return false;\n }\n\n // Union types are currently not allowed, because we have not seen the need for it.\n // Technically narrowing (removing `| Type` or subtyping) could be allowed and this works in C#.\n if (spec.isUnionTypeReference(subType) || spec.isUnionTypeReference(superType)) {\n return false;\n }\n\n // Intersection types are invalid, because intersections are only allowed as inputs\n // and covariance is only allowed in outputs.\n if (spec.isIntersectionTypeReference(subType) || spec.isIntersectionTypeReference(superType)) {\n return false;\n }\n\n // Primitives can never be covariant to each other in C#\n if (spec.isPrimitiveTypeReference(subType) || spec.isPrimitiveTypeReference(superType)) {\n return false;\n }\n\n // We really only support covariance for named types (and lists of named types).\n // To be safe, let's guard against any unknown cases.\n if (!spec.isNamedTypeReference(subType) || !spec.isNamedTypeReference(superType)) {\n return false;\n }\n\n const subTypeSpec = dereference(subType.fqn);\n const superTypeSpec = dereference(superType.fqn);\n\n if (!subTypeSpec || !superTypeSpec) {\n return false;\n }\n\n // Handle class-to-class inheritance\n if (spec.isClassType(subTypeSpec) && spec.isClassType(superTypeSpec)) {\n return _classExtendsClass(subTypeSpec, superType.fqn);\n }\n\n // Handle interface-to-interface inheritance\n if (spec.isInterfaceType(subTypeSpec) && spec.isInterfaceType(superTypeSpec)) {\n return _interfaceExtendsInterface(subTypeSpec, superType.fqn);\n }\n\n // Handle class implementing interface\n if (spec.isClassType(subTypeSpec) && spec.isInterfaceType(superTypeSpec)) {\n return _classImplementsInterface(subTypeSpec, superType.fqn);\n }\n\n return false;\n\n function _classExtendsClass(classType: spec.ClassType, targetFqn: string): boolean {\n let current = classType;\n while (current.base) {\n if (current.base === targetFqn) {\n return true;\n }\n const baseType = dereference(current.base);\n if (!spec.isClassType(baseType)) {\n break;\n }\n current = baseType;\n }\n return false;\n }\n\n function _classImplementsInterface(classType: spec.ClassType, interfaceFqn: string): boolean {\n // Check direct interfaces\n if (classType.interfaces?.includes(interfaceFqn)) {\n return true;\n }\n\n // Check inherited interfaces\n if (classType.interfaces) {\n for (const iface of classType.interfaces) {\n const ifaceType = dereference(iface);\n if (spec.isInterfaceType(ifaceType) && _interfaceExtendsInterface(ifaceType, interfaceFqn)) {\n return true;\n }\n }\n }\n\n // Check base class interfaces\n if (classType.base) {\n const baseType = dereference(classType.base);\n if (spec.isClassType(baseType)) {\n return _classImplementsInterface(baseType, interfaceFqn);\n }\n }\n\n return false;\n }\n\n function _interfaceExtendsInterface(interfaceType: spec.InterfaceType, targetFqn: string): boolean {\n if (interfaceType.fqn === targetFqn) {\n return true;\n }\n\n if (interfaceType.interfaces) {\n for (const iface of interfaceType.interfaces) {\n if (iface === targetFqn) {\n return true;\n }\n const ifaceType = dereference(iface);\n if (spec.isInterfaceType(ifaceType) && _interfaceExtendsInterface(ifaceType, targetFqn)) {\n return true;\n }\n }\n }\n\n return false;\n }\n}\n"]}
@@ -0,0 +1,20 @@
1
+ import * as spec from '@jsii/spec';
2
+ /**
3
+ * Convert a type reference to a string
4
+ */
5
+ export declare function typeReferenceToString(x: spec.TypeReference): string;
6
+ /**
7
+ * Return whether the given type references are equal
8
+ */
9
+ export declare function typeReferenceEqual(a: spec.TypeReference, b: spec.TypeReference): boolean;
10
+ export type TypeResolver = (typeRef: string | spec.NamedTypeReference) => spec.Type | undefined;
11
+ /**
12
+ * Creates a type resolver function for a given context (assembly + dependency closure).
13
+ */
14
+ export declare function createTypeResolver(assembly: spec.Assembly, dependencyClosure: readonly spec.Assembly[]): TypeResolver;
15
+ /**
16
+ * Resolve a type from a name to the actual type.
17
+ * Uses a given assembly and dependency closure for lookup.
18
+ */
19
+ export declare function resolveType(typeRef: string | spec.NamedTypeReference, assembly: spec.Assembly, dependencyClosure: readonly spec.Assembly[]): spec.Type | undefined;
20
+ //# sourceMappingURL=type-reference.d.ts.map
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.typeReferenceToString = typeReferenceToString;
4
+ exports.typeReferenceEqual = typeReferenceEqual;
5
+ exports.createTypeResolver = createTypeResolver;
6
+ exports.resolveType = resolveType;
7
+ const spec = require("@jsii/spec");
8
+ const type_visitor_1 = require("./type-visitor");
9
+ /**
10
+ * Convert a type reference to a string
11
+ */
12
+ function typeReferenceToString(x) {
13
+ return (0, type_visitor_1.visitTypeReference)(x, {
14
+ named: function (ref) {
15
+ return ref.fqn;
16
+ },
17
+ primitive: function (ref) {
18
+ return ref.primitive;
19
+ },
20
+ collection: function (ref) {
21
+ return `${ref.collection.kind}<${typeReferenceToString(ref.collection.elementtype)}>`;
22
+ },
23
+ union: function (ref) {
24
+ return ref.union.types.map(typeReferenceToString).join(' | ');
25
+ },
26
+ intersection: function (ref) {
27
+ return ref.intersection.types.map(typeReferenceToString).join(' & ');
28
+ },
29
+ });
30
+ }
31
+ /**
32
+ * Return whether the given type references are equal
33
+ */
34
+ function typeReferenceEqual(a, b) {
35
+ if (spec.isNamedTypeReference(a) && spec.isNamedTypeReference(b)) {
36
+ return a.fqn === b.fqn;
37
+ }
38
+ if (spec.isPrimitiveTypeReference(a) && spec.isPrimitiveTypeReference(b)) {
39
+ return a.primitive === b.primitive;
40
+ }
41
+ if (spec.isCollectionTypeReference(a) && spec.isCollectionTypeReference(b)) {
42
+ return (a.collection.kind === b.collection.kind && typeReferenceEqual(a.collection.elementtype, b.collection.elementtype));
43
+ }
44
+ if (spec.isUnionTypeReference(a) && spec.isUnionTypeReference(b)) {
45
+ return (a.union.types.length === b.union.types.length &&
46
+ a.union.types.every((aType, i) => typeReferenceEqual(aType, b.union.types[i])));
47
+ }
48
+ if (spec.isIntersectionTypeReference(a) && spec.isIntersectionTypeReference(b)) {
49
+ return (a.intersection.types.length === b.intersection.types.length &&
50
+ a.intersection.types.every((aType, i) => typeReferenceEqual(aType, b.intersection.types[i])));
51
+ }
52
+ return false;
53
+ }
54
+ /**
55
+ * Creates a type resolver function for a given context (assembly + dependency closure).
56
+ */
57
+ function createTypeResolver(assembly, dependencyClosure) {
58
+ return (typeRef) => resolveType(typeRef, assembly, dependencyClosure);
59
+ }
60
+ /**
61
+ * Resolve a type from a name to the actual type.
62
+ * Uses a given assembly and dependency closure for lookup.
63
+ */
64
+ function resolveType(typeRef, assembly, dependencyClosure) {
65
+ if (typeof typeRef !== 'string') {
66
+ typeRef = typeRef.fqn;
67
+ }
68
+ const [assm] = typeRef.split('.');
69
+ if (assembly.name === assm) {
70
+ return assembly.types?.[typeRef];
71
+ }
72
+ const foreignAssm = dependencyClosure.find((dep) => dep.name === assm);
73
+ return foreignAssm?.types?.[typeRef];
74
+ }
75
+ //# sourceMappingURL=type-reference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-reference.js","sourceRoot":"","sources":["../src/type-reference.ts"],"names":[],"mappings":"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","sourcesContent":["import * as spec from '@jsii/spec';\nimport { visitTypeReference } from './type-visitor';\n\n/**\n * Convert a type reference to a string\n */\nexport function typeReferenceToString(x: spec.TypeReference): string {\n return visitTypeReference<string>(x, {\n named: function (ref: spec.NamedTypeReference) {\n return ref.fqn;\n },\n primitive: function (ref: spec.PrimitiveTypeReference) {\n return ref.primitive;\n },\n collection: function (ref: spec.CollectionTypeReference) {\n return `${ref.collection.kind}<${typeReferenceToString(ref.collection.elementtype)}>`;\n },\n union: function (ref: spec.UnionTypeReference) {\n return ref.union.types.map(typeReferenceToString).join(' | ');\n },\n intersection: function (ref: spec.IntersectionTypeReference) {\n return ref.intersection.types.map(typeReferenceToString).join(' & ');\n },\n });\n}\n\n/**\n * Return whether the given type references are equal\n */\nexport function typeReferenceEqual(a: spec.TypeReference, b: spec.TypeReference): boolean {\n if (spec.isNamedTypeReference(a) && spec.isNamedTypeReference(b)) {\n return a.fqn === b.fqn;\n }\n if (spec.isPrimitiveTypeReference(a) && spec.isPrimitiveTypeReference(b)) {\n return a.primitive === b.primitive;\n }\n if (spec.isCollectionTypeReference(a) && spec.isCollectionTypeReference(b)) {\n return (\n a.collection.kind === b.collection.kind && typeReferenceEqual(a.collection.elementtype, b.collection.elementtype)\n );\n }\n if (spec.isUnionTypeReference(a) && spec.isUnionTypeReference(b)) {\n return (\n a.union.types.length === b.union.types.length &&\n a.union.types.every((aType, i) => typeReferenceEqual(aType, b.union.types[i]))\n );\n }\n if (spec.isIntersectionTypeReference(a) && spec.isIntersectionTypeReference(b)) {\n return (\n a.intersection.types.length === b.intersection.types.length &&\n a.intersection.types.every((aType, i) => typeReferenceEqual(aType, b.intersection.types[i]))\n );\n }\n return false;\n}\n\nexport type TypeResolver = (typeRef: string | spec.NamedTypeReference) => spec.Type | undefined;\n\n/**\n * Creates a type resolver function for a given context (assembly + dependency closure).\n */\nexport function createTypeResolver(assembly: spec.Assembly, dependencyClosure: readonly spec.Assembly[]): TypeResolver {\n return (typeRef) => resolveType(typeRef, assembly, dependencyClosure);\n}\n\n/**\n * Resolve a type from a name to the actual type.\n * Uses a given assembly and dependency closure for lookup.\n */\nexport function resolveType(\n typeRef: string | spec.NamedTypeReference,\n assembly: spec.Assembly,\n dependencyClosure: readonly spec.Assembly[],\n): spec.Type | undefined {\n if (typeof typeRef !== 'string') {\n typeRef = typeRef.fqn;\n }\n const [assm] = typeRef.split('.');\n if (assembly.name === assm) {\n return assembly.types?.[typeRef];\n }\n const foreignAssm = dependencyClosure.find((dep) => dep.name === assm);\n return foreignAssm?.types?.[typeRef];\n}\n"]}
@@ -0,0 +1,19 @@
1
+ import * as spec from '@jsii/spec';
2
+ export interface TypeReferenceVisitor<A = void> {
3
+ named(ref: spec.NamedTypeReference): A;
4
+ primitive(ref: spec.PrimitiveTypeReference): A;
5
+ collection(ref: spec.CollectionTypeReference): A;
6
+ union(ref: spec.UnionTypeReference): A;
7
+ intersection(ref: spec.IntersectionTypeReference): A;
8
+ }
9
+ export declare function visitTypeReference<A>(typeRef: spec.TypeReference, visitor: TypeReferenceVisitor<A>): A;
10
+ export interface TypeVisitor<A = void> {
11
+ classType(t: spec.ClassType): A;
12
+ interfaceType(t: spec.InterfaceType): A;
13
+ dataType(t: spec.InterfaceType): A;
14
+ enumType(t: spec.EnumType): A;
15
+ }
16
+ export declare function visitType<A>(t: spec.Type, visitor: TypeVisitor<A>): A;
17
+ export declare function isDataType(t: spec.Type): t is spec.InterfaceType;
18
+ export declare function isBehavioralInterfaceType(t: spec.Type): t is spec.InterfaceType;
19
+ //# sourceMappingURL=type-visitor.d.ts.map
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.visitTypeReference = visitTypeReference;
4
+ exports.visitType = visitType;
5
+ exports.isDataType = isDataType;
6
+ exports.isBehavioralInterfaceType = isBehavioralInterfaceType;
7
+ const spec = require("@jsii/spec");
8
+ function visitTypeReference(typeRef, visitor) {
9
+ if (spec.isNamedTypeReference(typeRef)) {
10
+ return visitor.named(typeRef);
11
+ }
12
+ else if (spec.isPrimitiveTypeReference(typeRef)) {
13
+ return visitor.primitive(typeRef);
14
+ }
15
+ else if (spec.isCollectionTypeReference(typeRef)) {
16
+ return visitor.collection(typeRef);
17
+ }
18
+ else if (spec.isUnionTypeReference(typeRef)) {
19
+ return visitor.union(typeRef);
20
+ }
21
+ else if (spec.isIntersectionTypeReference(typeRef)) {
22
+ return visitor.intersection(typeRef);
23
+ }
24
+ else {
25
+ throw new Error(`Unknown type reference: ${JSON.stringify(typeRef)}`);
26
+ }
27
+ }
28
+ function visitType(t, visitor) {
29
+ if (spec.isClassType(t)) {
30
+ return visitor.classType(t);
31
+ }
32
+ else if (spec.isInterfaceType(t) && t.datatype) {
33
+ return visitor.dataType(t);
34
+ }
35
+ else if (spec.isInterfaceType(t)) {
36
+ return visitor.interfaceType(t);
37
+ }
38
+ else if (spec.isEnumType(t)) {
39
+ return visitor.enumType(t);
40
+ }
41
+ else {
42
+ throw new Error(`Unknown type: ${JSON.stringify(t)}`);
43
+ }
44
+ }
45
+ function isDataType(t) {
46
+ return spec.isInterfaceType(t) && !!t.datatype;
47
+ }
48
+ function isBehavioralInterfaceType(t) {
49
+ return spec.isInterfaceType(t) && !t.datatype;
50
+ }
51
+ //# sourceMappingURL=type-visitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-visitor.js","sourceRoot":"","sources":["../src/type-visitor.ts"],"names":[],"mappings":";;AAUA,gDAcC;AASD,8BAYC;AAED,gCAEC;AAED,8DAEC;AArDD,mCAAmC;AAUnC,SAAgB,kBAAkB,CAAI,OAA2B,EAAE,OAAgC;IACjG,IAAI,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,IAAI,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC;QAClD,OAAO,OAAO,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;SAAM,IAAI,IAAI,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,CAAC;QACnD,OAAO,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;SAAM,IAAI,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9C,OAAO,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,IAAI,CAAC,2BAA2B,CAAC,OAAO,CAAC,EAAE,CAAC;QACrD,OAAO,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACvC,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACxE,CAAC;AACH,CAAC;AASD,SAAgB,SAAS,CAAI,CAAY,EAAE,OAAuB;IAChE,IAAI,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;QACxB,OAAO,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;QACjD,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;SAAM,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;QACnC,OAAO,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;SAAM,IAAI,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9B,OAAO,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC7B,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IACxD,CAAC;AACH,CAAC;AAED,SAAgB,UAAU,CAAC,CAAY;IACrC,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;AACjD,CAAC;AAED,SAAgB,yBAAyB,CAAC,CAAY;IACpD,OAAO,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;AAChD,CAAC","sourcesContent":["import * as spec from '@jsii/spec';\n\nexport interface TypeReferenceVisitor<A = void> {\n named(ref: spec.NamedTypeReference): A;\n primitive(ref: spec.PrimitiveTypeReference): A;\n collection(ref: spec.CollectionTypeReference): A;\n union(ref: spec.UnionTypeReference): A;\n intersection(ref: spec.IntersectionTypeReference): A;\n}\n\nexport function visitTypeReference<A>(typeRef: spec.TypeReference, visitor: TypeReferenceVisitor<A>) {\n if (spec.isNamedTypeReference(typeRef)) {\n return visitor.named(typeRef);\n } else if (spec.isPrimitiveTypeReference(typeRef)) {\n return visitor.primitive(typeRef);\n } else if (spec.isCollectionTypeReference(typeRef)) {\n return visitor.collection(typeRef);\n } else if (spec.isUnionTypeReference(typeRef)) {\n return visitor.union(typeRef);\n } else if (spec.isIntersectionTypeReference(typeRef)) {\n return visitor.intersection(typeRef);\n } else {\n throw new Error(`Unknown type reference: ${JSON.stringify(typeRef)}`);\n }\n}\n\nexport interface TypeVisitor<A = void> {\n classType(t: spec.ClassType): A;\n interfaceType(t: spec.InterfaceType): A;\n dataType(t: spec.InterfaceType): A;\n enumType(t: spec.EnumType): A;\n}\n\nexport function visitType<A>(t: spec.Type, visitor: TypeVisitor<A>) {\n if (spec.isClassType(t)) {\n return visitor.classType(t);\n } else if (spec.isInterfaceType(t) && t.datatype) {\n return visitor.dataType(t);\n } else if (spec.isInterfaceType(t)) {\n return visitor.interfaceType(t);\n } else if (spec.isEnumType(t)) {\n return visitor.enumType(t);\n } else {\n throw new Error(`Unknown type: ${JSON.stringify(t)}`);\n }\n}\n\nexport function isDataType(t: spec.Type): t is spec.InterfaceType {\n return spec.isInterfaceType(t) && !!t.datatype;\n}\n\nexport function isBehavioralInterfaceType(t: spec.Type): t is spec.InterfaceType {\n return spec.isInterfaceType(t) && !t.datatype;\n}\n"]}
@@ -3,13 +3,17 @@ import * as ts from 'typescript';
3
3
  import { Emitter } from './emitter';
4
4
  import { JsiiDiagnostic } from './jsii-diagnostic';
5
5
  import { ProjectInfo } from './project-info';
6
+ export interface ValidationResult extends ts.EmitResult {
7
+ readonly usedFeatures?: spec.JsiiFeature[];
8
+ }
6
9
  export declare class Validator implements Emitter {
7
10
  readonly projectInfo: ProjectInfo;
8
11
  readonly assembly: spec.Assembly;
9
12
  static VALIDATIONS: ValidationFunction[];
10
13
  constructor(projectInfo: ProjectInfo, assembly: spec.Assembly);
11
- emit(): ts.EmitResult;
14
+ emit(): ValidationResult;
12
15
  }
13
16
  export type DiagnosticEmitter = (diag: JsiiDiagnostic) => void;
14
- export type ValidationFunction = (validator: Validator, assembly: spec.Assembly, diagnostic: DiagnosticEmitter) => void;
17
+ export type FeatureTracker = (feat: spec.JsiiFeature) => void;
18
+ export type ValidationFunction = (validator: Validator, assembly: spec.Assembly, diagnostic: DiagnosticEmitter, useFeatures: FeatureTracker) => void;
15
19
  //# sourceMappingURL=validator.d.ts.map
package/lib/validator.js CHANGED
@@ -9,6 +9,8 @@ const Case = require("./case");
9
9
  const jsii_diagnostic_1 = require("./jsii-diagnostic");
10
10
  const node_bindings_1 = require("./node-bindings");
11
11
  const bindings = require("./node-bindings");
12
+ const type_analysis_1 = require("./type-analysis");
13
+ const type_reference_1 = require("./type-reference");
12
14
  class Validator {
13
15
  constructor(projectInfo, assembly) {
14
16
  this.projectInfo = projectInfo;
@@ -16,12 +18,14 @@ class Validator {
16
18
  }
17
19
  emit() {
18
20
  const diagnostics = new Array();
21
+ const usedFeatures = new Array();
19
22
  for (const validation of Validator.VALIDATIONS) {
20
- validation(this, this.assembly, diagnostics.push.bind(diagnostics));
23
+ validation(this, this.assembly, diagnostics.push.bind(diagnostics), usedFeatures.push.bind(usedFeatures));
21
24
  }
22
25
  return {
23
26
  diagnostics: diagnostics,
24
27
  emitSkipped: diagnostics.some((diag) => diag.category === ts.DiagnosticCategory.Error),
28
+ usedFeatures,
25
29
  };
26
30
  }
27
31
  }
@@ -34,7 +38,7 @@ function _defaultValidations() {
34
38
  _staticConstantNamesMustUseUpperSnakeCase,
35
39
  _memberNamesMustNotLookLikeJavaGettersOrSetters,
36
40
  _allTypeReferencesAreValid,
37
- _inehritanceDoesNotChangeContracts,
41
+ _inheritanceDoesNotChangeContracts,
38
42
  _staticMembersAndNestedTypesMustNotSharePascalCaseName,
39
43
  _abstractClassesMustImplementAllProperties,
40
44
  ];
@@ -107,7 +111,8 @@ function _defaultValidations() {
107
111
  }
108
112
  }
109
113
  }
110
- function _inehritanceDoesNotChangeContracts(validator, assembly, diagnostic) {
114
+ function _inheritanceDoesNotChangeContracts(validator, assembly, diagnostic, useFeature) {
115
+ const _dereference = (0, type_reference_1.createTypeResolver)(assembly, validator.projectInfo.dependencyClosure);
111
116
  for (const type of _allTypes(assembly)) {
112
117
  if (spec.isClassType(type)) {
113
118
  for (const method of type.methods ?? []) {
@@ -147,7 +152,7 @@ function _defaultValidations() {
147
152
  }
148
153
  if (spec.isClassType(type) && type.base) {
149
154
  // We have a parent class, collect their concrete members, too (recursively)...
150
- const base = _dereference(type.base, assembly, validator);
155
+ const base = _dereference(type.base);
151
156
  assert(base != null && spec.isClassType(base));
152
157
  for (const member of _allImplementations(base, getter)) {
153
158
  if (known.has(member.name)) {
@@ -175,7 +180,7 @@ function _defaultValidations() {
175
180
  if (!type.base) {
176
181
  return false;
177
182
  }
178
- const baseType = _dereference(type.base, assembly, validator);
183
+ const baseType = _dereference(type.base);
179
184
  if (!baseType) {
180
185
  return false;
181
186
  }
@@ -183,7 +188,9 @@ function _defaultValidations() {
183
188
  if (!overridden) {
184
189
  return _validateMethodOverride(method, baseType);
185
190
  }
186
- _assertSignaturesMatch(overridden, method, `${type.fqn}#${method.name}`, `overriding ${baseType.fqn}`);
191
+ _assertSignaturesMatch(overridden, method, `${type.fqn}#${method.name}`, `overriding ${baseType.fqn}`, {
192
+ allowReturnTypeCovariance: true,
193
+ });
187
194
  method.overrides = baseType.fqn;
188
195
  return true;
189
196
  }
@@ -191,7 +198,7 @@ function _defaultValidations() {
191
198
  if (!type.base) {
192
199
  return false;
193
200
  }
194
- const baseType = _dereference(type.base, assembly, validator);
201
+ const baseType = _dereference(type.base);
195
202
  if (!baseType) {
196
203
  return false;
197
204
  }
@@ -199,7 +206,9 @@ function _defaultValidations() {
199
206
  if (!overridden) {
200
207
  return _validatePropertyOverride(property, baseType);
201
208
  }
202
- _assertPropertiesMatch(overridden, property, `${type.fqn}#${property.name}`, `overriding ${baseType.fqn}`);
209
+ _assertPropertiesMatch(overridden, property, `${type.fqn}#${property.name}`, `overriding ${baseType.fqn}`, {
210
+ allowCovariance: true,
211
+ });
203
212
  property.overrides = baseType.fqn;
204
213
  return true;
205
214
  }
@@ -207,15 +216,17 @@ function _defaultValidations() {
207
216
  if (!type.interfaces) {
208
217
  // Abstract classes may not directly implement all members, need to check their supertypes...
209
218
  if (spec.isClassType(type) && type.base && type.abstract) {
210
- return _validateMethodImplementation(method, _dereference(type.base, assembly, validator));
219
+ return _validateMethodImplementation(method, _dereference(type.base));
211
220
  }
212
221
  return false;
213
222
  }
214
223
  for (const iface of type.interfaces) {
215
- const ifaceType = _dereference(iface, assembly, validator);
224
+ const ifaceType = _dereference(iface);
216
225
  const implemented = (ifaceType.methods ?? []).find((m) => m.name === method.name);
217
226
  if (implemented) {
218
- _assertSignaturesMatch(implemented, method, `${type.fqn}#${method.name}`, `implementing ${ifaceType.fqn}`);
227
+ _assertSignaturesMatch(implemented, method, `${type.fqn}#${method.name}`, `implementing ${ifaceType.fqn}`, {
228
+ allowReturnTypeCovariance: false,
229
+ });
219
230
  // We won't replace a previous overrides declaration from a method override, as those have
220
231
  // higher precedence than an initial implementation.
221
232
  method.overrides = method.overrides ?? iface;
@@ -231,15 +242,15 @@ function _defaultValidations() {
231
242
  if (!type.interfaces) {
232
243
  // Abstract classes may not directly implement all members, need to check their supertypes...
233
244
  if (spec.isClassType(type) && type.base && type.abstract) {
234
- return _validatePropertyImplementation(property, _dereference(type.base, assembly, validator));
245
+ return _validatePropertyImplementation(property, _dereference(type.base));
235
246
  }
236
247
  return false;
237
248
  }
238
249
  for (const iface of type.interfaces) {
239
- const ifaceType = _dereference(iface, assembly, validator);
250
+ const ifaceType = _dereference(iface);
240
251
  const implemented = (ifaceType.properties ?? []).find((p) => p.name === property.name);
241
252
  if (implemented) {
242
- _assertPropertiesMatch(implemented, property, `${type.fqn}#${property.name}`, `implementing ${ifaceType.fqn}`);
253
+ _assertPropertiesMatch(implemented, property, `${type.fqn}#${property.name}`, `implementing ${ifaceType.fqn}`, { allowCovariance: false });
243
254
  // We won't replace a previous overrides declaration from a property override, as those
244
255
  // have higher precedence than an initial implementation.
245
256
  property.overrides = property.overrides ?? ifaceType.fqn;
@@ -251,16 +262,30 @@ function _defaultValidations() {
251
262
  }
252
263
  return false;
253
264
  }
254
- function _assertSignaturesMatch(expected, actual, label, action) {
265
+ function _assertSignaturesMatch(expected, actual, label, action, opts) {
255
266
  if (!!expected.protected !== !!actual.protected) {
256
267
  const expVisibility = expected.protected ? 'protected' : 'public';
257
268
  const actVisibility = actual.protected ? 'protected' : 'public';
258
269
  diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5002_OVERRIDE_CHANGES_VISIBILITY.createDetached(label, action, actVisibility, expVisibility));
259
270
  }
271
+ // Types must generally be the same, but can be covariant sometimes
260
272
  if (!deepEqual(actual.returns, expected.returns)) {
261
- const expType = spec.describeTypeReference(expected.returns?.type);
262
- const actType = spec.describeTypeReference(actual.returns?.type);
263
- diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5003_OVERRIDE_CHANGES_RETURN_TYPE.createDetached(label, action, actType, expType));
273
+ const actualReturnType = actual.returns?.type;
274
+ const expectedReturnType = expected.returns?.type;
275
+ if (
276
+ // return type covariance is allowed
277
+ opts.allowReturnTypeCovariance &&
278
+ // static members can never change
279
+ !actual.static &&
280
+ // this is a valid covariant return type (actual is more specific than expected)
281
+ (0, type_analysis_1.isAllowedCovariantSubtype)(actualReturnType, expectedReturnType, _dereference)) {
282
+ useFeature('class-covariant-overrides');
283
+ }
284
+ else {
285
+ const expType = spec.describeTypeReference(expectedReturnType);
286
+ const actType = spec.describeTypeReference(actualReturnType);
287
+ diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5003_OVERRIDE_CHANGES_RETURN_TYPE.createDetached(label, action, actType, expType));
288
+ }
264
289
  }
265
290
  const expectedParams = expected.parameters ?? [];
266
291
  const actualParams = actual.parameters ?? [];
@@ -283,7 +308,7 @@ function _defaultValidations() {
283
308
  }
284
309
  }
285
310
  }
286
- function _assertPropertiesMatch(expected, actual, label, action) {
311
+ function _assertPropertiesMatch(expected, actual, label, action, opts) {
287
312
  const actualNode = bindings.getPropertyRelatedNode(actual);
288
313
  const expectedNode = bindings.getPropertyRelatedNode(expected);
289
314
  if (!!expected.protected !== !!actual.protected) {
@@ -291,8 +316,21 @@ function _defaultValidations() {
291
316
  const actVisibility = actual.protected ? 'protected' : 'public';
292
317
  diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5002_OVERRIDE_CHANGES_VISIBILITY.create(actualNode?.modifiers?.find((mod) => mod.kind === ts.SyntaxKind.PublicKeyword || mod.kind === ts.SyntaxKind.ProtectedKeyword) ?? declarationName(actualNode), label, action, actVisibility, expVisibility).maybeAddRelatedInformation(expectedNode?.modifiers?.find((mod) => mod.kind === ts.SyntaxKind.PublicKeyword || mod.kind === ts.SyntaxKind.ProtectedKeyword) ?? declarationName(expectedNode), 'The implemented declaration is here.'));
293
318
  }
294
- if (!deepEqual(expected.type, actual.type)) {
295
- diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5004_OVERRIDE_CHANGES_PROP_TYPE.create(actualNode?.type ?? declarationName(actualNode), label, action, actual.type, expected.type).maybeAddRelatedInformation(expectedNode?.type ?? declarationName(expectedNode), 'The implemented declaration is here.'));
319
+ // Types must generally be the same, but can be covariant sometimes
320
+ if (!deepEqual(actual.type, expected.type)) {
321
+ if (
322
+ // return type covariance is allowed
323
+ opts.allowCovariance &&
324
+ // static members can never change
325
+ !actual.static &&
326
+ // immutable properties may change in some case, as long as they are covariant
327
+ actual.immutable &&
328
+ (0, type_analysis_1.isAllowedCovariantSubtype)(actual.type, expected.type, _dereference)) {
329
+ useFeature('class-covariant-overrides');
330
+ }
331
+ else {
332
+ diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5004_OVERRIDE_CHANGES_PROP_TYPE.create(actualNode?.type ?? declarationName(actualNode), label, action, actual.type, expected.type).maybeAddRelatedInformation(expectedNode?.type ?? declarationName(expectedNode), 'The implemented declaration is here.'));
333
+ }
296
334
  }
297
335
  if (expected.immutable !== actual.immutable) {
298
336
  diagnostic(jsii_diagnostic_1.JsiiDiagnostic.JSII_5010_OVERRIDE_CHANGES_MUTABILITY.create(actualNode?.modifiers?.find((mod) => mod.kind === ts.SyntaxKind.ReadonlyKeyword) ??
@@ -316,6 +354,7 @@ function _defaultValidations() {
316
354
  * don't want to give an implementation yet.
317
355
  */
318
356
  function _abstractClassesMustImplementAllProperties(validator, assembly, diagnostic) {
357
+ const _dereference = (0, type_reference_1.createTypeResolver)(assembly, validator.projectInfo.dependencyClosure);
319
358
  for (const type of _allTypes(assembly)) {
320
359
  if (!spec.isClassType(type) || !type.abstract) {
321
360
  continue;
@@ -333,7 +372,7 @@ function _defaultValidations() {
333
372
  into.add(prop.name);
334
373
  }
335
374
  if (type.base) {
336
- const base = _dereference(type.base, assembly, validator);
375
+ const base = _dereference(type.base);
337
376
  if (spec.isClassType(base)) {
338
377
  collectClassProps(base, into);
339
378
  }
@@ -341,7 +380,7 @@ function _defaultValidations() {
341
380
  return into;
342
381
  }
343
382
  function checkInterfacePropsImplemented(interfaceFqn, cls, propNames) {
344
- const intf = _dereference(interfaceFqn, assembly, validator);
383
+ const intf = _dereference(interfaceFqn);
345
384
  if (!spec.isInterfaceType(intf)) {
346
385
  return;
347
386
  }
@@ -482,17 +521,6 @@ function _allTypeReferences(assm) {
482
521
  }
483
522
  }
484
523
  }
485
- function _dereference(typeRef, assembly, validator) {
486
- if (typeof typeRef !== 'string') {
487
- typeRef = typeRef.fqn;
488
- }
489
- const [assm] = typeRef.split('.');
490
- if (assembly.name === assm) {
491
- return assembly.types?.[typeRef];
492
- }
493
- const foreignAssm = validator.projectInfo.dependencyClosure.find((dep) => dep.name === assm);
494
- return foreignAssm?.types?.[typeRef];
495
- }
496
524
  function _isEmpty(array) {
497
525
  return array == null || array.length === 0;
498
526
  }