Skip to content

Commit 72927bd

Browse files
author
Elad Ben-Israel
authored
Fix(jsii): improve diagnostics when dereference fails (#222)
In some cases, when a type could not be referenced, the diagnostic message doesn't indicate that (the undefined return value will still cause an error, but we also want to know that we couldn't deref the type). Fixes #221
1 parent e80a889 commit 72927bd

2 files changed

Lines changed: 40 additions & 28 deletions

File tree

packages/jsii/lib/assembler.ts

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -162,14 +162,21 @@ export class Assembler implements Emitter {
162162
*
163163
* @returns the de-referenced type, if it was found, otherwise ``undefined``.
164164
*/
165-
private _dereference(ref: spec.NamedTypeReference): spec.Type | undefined {
165+
private _dereference(ref: spec.NamedTypeReference, referencingNode: ts.Node): spec.Type | undefined {
166166
const [assm, ] = ref.fqn.split('.');
167+
let type;
167168
if (assm === this.projectInfo.name) {
168-
return this._types[ref.fqn];
169+
type = this._types[ref.fqn];
169170
} else {
170171
const assembly = this.projectInfo.transitiveDependencies.find(dep => dep.name === assm);
171-
return assembly && assembly.types && assembly.types[ref.fqn];
172+
type = assembly && assembly.types && assembly.types[ref.fqn];
172173
}
174+
175+
if (!type) {
176+
this._diagnostic(referencingNode, ts.DiagnosticCategory.Error, `Unable to resolve referenced type '${ref.fqn}'. Missing export?`);
177+
}
178+
179+
return type;
173180
}
174181

175182
private _diagnostic(node: ts.Node | null, category: ts.DiagnosticCategory, messageText: string) {
@@ -206,7 +213,7 @@ export class Assembler implements Emitter {
206213
return `unknown.${typeName}`;
207214
}
208215
const fqn = `${pkg.name}.${typeName}`;
209-
if (pkg.name !== this.projectInfo.name && !this._dereference({ fqn })) {
216+
if (pkg.name !== this.projectInfo.name && !this._dereference({ fqn }, type.symbol.valueDeclaration)) {
210217
this._diagnostic(type.symbol.valueDeclaration,
211218
ts.DiagnosticCategory.Error,
212219
`Use of foreign type not present in the ${pkg.name}'s assembly: ${fqn}`);
@@ -314,7 +321,8 @@ export class Assembler implements Emitter {
314321
continue;
315322
}
316323
this._defer(() => {
317-
if (!spec.isClassType(this._dereference(ref))) {
324+
const deref = this._dereference(ref, base.symbol.valueDeclaration);
325+
if (deref && !spec.isClassType(deref)) {
318326
this._diagnostic(base.symbol.valueDeclaration,
319327
ts.DiagnosticCategory.Error,
320328
`Base type of ${jsiiType.fqn} is not a class (${spec.describeTypeReference(ref)})`);
@@ -340,7 +348,8 @@ export class Assembler implements Emitter {
340348
continue;
341349
}
342350
this._defer(() => {
343-
if (!spec.isInterfaceType(this._dereference(typeRef))) {
351+
const deref = this._dereference(typeRef, expression);
352+
if (deref && !spec.isInterfaceType(deref)) {
344353
this._diagnostic(expression,
345354
ts.DiagnosticCategory.Error,
346355
`Implements clause of ${jsiiType.fqn} uses ${spec.describeTypeReference(typeRef)} as an interface`);
@@ -393,17 +402,15 @@ export class Assembler implements Emitter {
393402
}
394403
} else if (jsiiType.base) {
395404
this._defer(() => {
396-
const baseType = this._dereference(jsiiType.base!);
397-
if (!baseType) {
398-
this._diagnostic(type.symbol.valueDeclaration,
399-
ts.DiagnosticCategory.Error,
400-
`Unable to resolve type ${jsiiType.base!.fqn} (base type of ${jsiiType.fqn})`);
401-
} else if (spec.isClassType(baseType)) {
402-
jsiiType.initializer = baseType.initializer;
403-
} else {
404-
this._diagnostic(type.symbol.valueDeclaration,
405-
ts.DiagnosticCategory.Error,
406-
`Base type of ${jsiiType.fqn} (${jsiiType.base!.fqn}) is not a class`);
405+
const baseType = this._dereference(jsiiType.base!, type.symbol.valueDeclaration);
406+
if (baseType) {
407+
if (spec.isClassType(baseType)) {
408+
jsiiType.initializer = baseType.initializer;
409+
} else {
410+
this._diagnostic(type.symbol.valueDeclaration,
411+
ts.DiagnosticCategory.Error,
412+
`Base type of ${jsiiType.fqn} (${jsiiType.base!.fqn}) is not a class`);
413+
}
407414
}
408415
});
409416
} else {
@@ -499,17 +506,13 @@ export class Assembler implements Emitter {
499506
continue;
500507
}
501508
this._defer(() => {
502-
if (!spec.isInterfaceType(this._dereference(ref))) {
503-
const baseType = this._dereference(ref);
504-
if (baseType) {
505-
this._diagnostic(base.symbol.valueDeclaration,
506-
ts.DiagnosticCategory.Error,
507-
`Base type of ${jsiiType.fqn} is not an interface (${baseType.kind} ${spec.describeTypeReference(ref)})`);
508-
} else {
509-
this._diagnostic(base.symbol.valueDeclaration,
510-
ts.DiagnosticCategory.Error,
511-
`Base type of ${jsiiType.fqn} could not be resolved (${spec.describeTypeReference(ref)})`);
512-
}
509+
const baseType = this._dereference(ref, base.symbol.valueDeclaration);
510+
if (baseType && !spec.isInterfaceType(baseType)) {
511+
// tslint:disable:max-line-length
512+
this._diagnostic(base.symbol.valueDeclaration,
513+
ts.DiagnosticCategory.Error,
514+
`Base type of ${jsiiType.fqn} is not an interface (${baseType.kind} ${spec.describeTypeReference(ref)})`);
515+
// tslint:enable:max-line-length
513516
}
514517
});
515518
if (jsiiType.interfaces) {
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
///!MATCH_ERROR: Unable to resolve referenced type 'Base'. Missing export?
2+
3+
class Base {
4+
5+
}
6+
7+
export class Derived extends Base {
8+
9+
}

0 commit comments

Comments
 (0)