Secure your code as it's written. Use Snyk Code to scan source code in minutes - no build needed - and fix issues immediately.
LOG.debug(`Base class of ${colors.green(jsiiType.fqn)} named ${colors.green(base.symbol.name)} is not exported, erasing it...`);
erasedBases.push(base);
base = (base.getBaseTypes() ?? [])[0];
}
if (!base) {
// There is no exported base class to be found, pretend this class has no base class.
continue;
}
// eslint-disable-next-line no-await-in-loop
const ref = await this._typeReference(base, type.symbol.valueDeclaration);
if (!spec.isNamedTypeReference(ref)) {
this._diagnostic(base.symbol.valueDeclaration,
ts.DiagnosticCategory.Error,
`Base type of ${jsiiType.fqn} is not a named type (${spec.describeTypeReference(ref)})`);
continue;
}
this._deferUntilTypesAvailable(fqn, [ref], base.symbol.valueDeclaration, (deref) => {
if (!spec.isClassType(deref)) {
this._diagnostic(base.symbol.valueDeclaration,
ts.DiagnosticCategory.Error,
`Base type of ${jsiiType.fqn} is not a class (${spec.describeTypeReference(ref)})`);
}
});
jsiiType.base = ref.fqn;
}
//
// base interfaces ("implements foo")
// collect all "implements" declarations from the current type and all
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes the return type when ${action} (expected ${expType}, found ${actType})`);
}
const expectedParams = expected.parameters ?? [];
const actualParams = actual.parameters ?? [];
if (expectedParams.length !== actualParams.length) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes argument count when ${action} (expected ${expectedParams.length}, found ${actualParams.length})`);
return;
}
for (let i = 0 ; i < expectedParams.length ; i++) {
const expParam = expectedParams[i];
const actParam = actualParams[i];
if (!deepEqual(expParam.type, actParam.type)) {
const expType = spec.describeTypeReference(expParam.type);
const actType = spec.describeTypeReference(actParam.type);
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes type of argument ${actParam.name} when ${action} (expected ${expType}, found ${actType}`);
}
// Not-ing those to force the values to a strictly boolean context (they're optional, undefined means false)
if (expParam.variadic !== actParam.variadic) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes the variadicity of parameter ${actParam.name} when ${action} (expected ${!!expParam.variadic}, found ${!!actParam.variadic})`);
}
if (expParam.optional !== actParam.optional) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes the optionality of paramerter ${actParam.name} when ${action} (expected ${!!expParam.optional}, found ${!!actParam.optional})`);
}
}
}
const params: spec.Parameter[] = method?.parameters ?? [];
// error if args > params
if (args.length > params.length && !(method && method.variadic)) {
throw new Error(`Too many arguments (method accepts ${params.length} parameters, got ${args.length} arguments)`);
}
for (let i = 0; i < params.length; ++i) {
const param = params[i];
const arg = args[i];
if (param.variadic) {
if (params.length <= i) { return; } // No vararg was provided
for (let j = i ; j < params.length ; j++) {
if (!param.optional && params[j] === undefined) {
throw new Error(`Unexpected 'undefined' value at index ${j - i} of variadic argument '${param.name}' of type '${spec.describeTypeReference(param.type)}'`);
}
}
} else if (!param.optional && arg === undefined) {
throw new Error(`Not enough arguments. Missing argument for the required parameter '${param.name}' of type '${spec.describeTypeReference(param.type)}'`);
}
}
}
baseInterfaces.add(iface);
}
};
processBaseTypes(baseTypes);
const typeRefs = Array.from(baseInterfaces).map(async iface => {
const decl = iface.symbol.valueDeclaration;
const typeRef = await this._typeReference(iface, decl);
return { decl, typeRef };
});
for (const { decl, typeRef } of await Promise.all(typeRefs)) {
if (!spec.isNamedTypeReference(typeRef)) {
this._diagnostic(decl,
ts.DiagnosticCategory.Error,
`Interface of ${fqn} is not a named type (${spec.describeTypeReference(typeRef)})`);
continue;
}
this._deferUntilTypesAvailable(fqn, [typeRef], decl, (deref) => {
if (!spec.isInterfaceType(deref)) {
this._diagnostic(decl,
ts.DiagnosticCategory.Error,
`Inheritance clause of ${fqn} uses ${spec.describeTypeReference(typeRef)} as an interface`);
}
});
result.push(typeRef);
}
return { interfaces: result.length === 0 ? undefined : result, erasedBases };
}
function _assertPropertiesMatch(expected: spec.Property, actual: spec.Property, label: string, action: string) {
if (!deepEqual(expected.type, actual.type)) {
const expType = spec.describeTypeReference(expected.type);
const actType = spec.describeTypeReference(actual.type);
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes the type of property when ${action} (expected ${expType}, found ${actType})`);
}
if (expected.immutable !== actual.immutable) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes immutability of property when ${action}`);
}
if (expected.optional !== actual.optional) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes optionality of property when ${action}`);
}
}
}
public addFromInterface(fqn: string): void {
const ti = this.resolveType(fqn);
if (!spec.isInterfaceType(ti)) {
throw new Error(`Expected an interface, but received ${spec.describeTypeReference(ti)}`);
}
if (!ti.interfaces) { return; }
for (const iface of ti.interfaces) {
if (this.interfaces.has(iface)) { continue; }
this.interfaces.add(iface);
this.addFromInterface(iface);
}
}
function _assertPropertiesMatch(expected: spec.Property, actual: spec.Property, label: string, action: string) {
if (!deepEqual(expected.type, actual.type)) {
const expType = spec.describeTypeReference(expected.type);
const actType = spec.describeTypeReference(actual.type);
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes the type of property when ${action} (expected ${expType}, found ${actType})`);
}
if (expected.immutable !== actual.immutable) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes immutability of property when ${action}`);
}
if (expected.optional !== actual.optional) {
diagnostic(ts.DiagnosticCategory.Error,
`${label} changes optionality of property when ${action}`);
}
}
}
public addFromClass(fqn: string): void {
const ti = this.resolveType(fqn);
if (!spec.isClassType(ti)) {
throw new Error(`Expected a class, but received ${spec.describeTypeReference(ti)}`);
}
if (ti.base) {
this.addFromClass(ti.base);
}
if (ti.interfaces) {
for (const iface of ti.interfaces) {
if (this.interfaces.has(iface)) {
continue;
}
this.interfaces.add(iface);
this.addFromInterface(iface);
}
}
}