Skip to content

Commit

Permalink
fix(jsii): TypeError: Cannot read property 'getJsDocTags' of undefined (
Browse files Browse the repository at this point in the history
#2163)

When a trailing semicolon was left after a method body, the compiler
would choke with a `TypeError` due to attempting to read the JSDoc tags
of an `undefined` symbol.

This checks for this particular case and ignores the "class member" that
is represented by the dangling semicolon; and emits a diagnostic message
informing users the token could be removed.

Fixes #2098



---

By submitting this pull request, I confirm that my contribution is made under the terms of the [Apache 2.0 license].

[Apache 2.0 license]: https://www.apache.org/licenses/LICENSE-2.0
  • Loading branch information
RomainMuller authored Oct 22, 2020
1 parent ce7599f commit 5d87101
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 7 deletions.
16 changes: 12 additions & 4 deletions packages/jsii/lib/assembler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1240,10 +1240,18 @@ export class Assembler implements Emitter {
}

for (const memberDecl of classDecl.members) {
// The "??" is to get to the __constructor symbol (getSymbolAtLocation wouldn't work there..)
const member =
this._typeChecker.getSymbolAtLocation(memberDecl.name!) ??
((memberDecl as any).symbol as ts.Symbol);
if (ts.isSemicolonClassElement(memberDecl)) {
this._diagnostics.push(
JsiiDiagnostic.JSII_9996_UNNECESSARY_TOKEN.create(memberDecl),
);
continue;
}

const member: ts.Symbol = ts.isConstructorDeclaration(memberDecl)
? (memberDecl as any).symbol
: this._typeChecker.getSymbolAtLocation(
ts.getNameOfDeclaration(memberDecl)!,
)!;

if (
!(declaringType.symbol.getDeclarations() ?? []).find(
Expand Down
5 changes: 4 additions & 1 deletion packages/jsii/lib/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as ts from 'typescript';

import { Assembler } from './assembler';
import { Emitter } from './emitter';
import { JsiiDiagnostic } from './jsii-diagnostic';
import { ProjectInfo } from './project-info';
import * as utils from './utils';

Expand Down Expand Up @@ -246,7 +247,9 @@ export class Compiler implements Emitter {

diagnostics.push(...assmEmit.diagnostics);
} catch (e) {
LOG.error(`Error during type model analysis: ${e}\n${e.stack}`);
diagnostics.push(
JsiiDiagnostic.JSII_9997_UNKNOWN_ERROR.createDetached(e),
);
hasErrors = true;
}

Expand Down
2 changes: 1 addition & 1 deletion packages/jsii/lib/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export async function compileJsiiForTest(
console.error(error.messageText);
// logDiagnostic() doesn't work out of the box, so console.error() it is.
}
if (errors.length > 0) {
if (errors.length > 0 || emitResult.emitSkipped) {
throw new Error('There were compiler errors');
}
const assembly = await fs.readJSON('.jsii', { encoding: 'utf-8' });
Expand Down
15 changes: 14 additions & 1 deletion packages/jsii/lib/jsii-diagnostic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -708,7 +708,7 @@ export class JsiiDiagnostic implements ts.Diagnostic {
public static readonly JSII_9002_UNRESOLVEABLE_TYPE = Code.error({
code: 9002,
formatter: (reference: string) =>
`Unable to resolve type "${reference}". It may be @iternal or not exported from the module's entry point (as configured in "package.json" as "main").`,
`Unable to resolve type "${reference}". It may be @internal or not exported from the module's entry point (as configured in "package.json" as "main").`,
name: 'miscellaneous/unresolveable-type',
});

Expand All @@ -726,6 +726,19 @@ export class JsiiDiagnostic implements ts.Diagnostic {
name: 'miscellaneous/unable-to-compute-signature',
});

public static readonly JSII_9996_UNNECESSARY_TOKEN = Code.message({
code: 9996,
formatter: () => 'Unnecessary token, consider removing it',
name: 'miscellaneous/unnecessary-token',
});

public static readonly JSII_9997_UNKNOWN_ERROR = Code.error({
code: 9997,
formatter: (error: Error) =>
`Unknown error: ${error.message} -- ${error.stack}`,
name: 'miscellaneous/unknown-error',
});

public static readonly JSII_9998_UNSUPORTED_NODE = Code.message({
code: 9998,
formatter: (kindOrMessage: ts.SyntaxKind | string) =>
Expand Down
32 changes: 32 additions & 0 deletions packages/jsii/test/quirks.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { sourceToAssemblyHelper } from '../lib';

// ----------------------------------------------------------------------
test('trailing semicolon after method is correctly ignored', async () => {
const assembly = await sourceToAssemblyHelper(`
export class Foo {
private readonly initialized: boolean;
public constructor() {
this.initialized = true;
};
public method() { return this.initialized; };
}
`);

expect(assembly.types!['testpkg.Foo']).toEqual({
assembly: 'testpkg',
fqn: 'testpkg.Foo',
kind: 'class',
methods: [
{
locationInModule: { filename: 'index.ts', line: 9 },
name: 'method',
returns: { type: { primitive: 'boolean' } },
},
],
initializer: { locationInModule: { filename: 'index.ts', line: 5 } },
locationInModule: { filename: 'index.ts', line: 2 },
name: 'Foo',
});
});

0 comments on commit 5d87101

Please sign in to comment.