fix(tools): harden colletor against invalid asts (#12793)
This commit is contained in:
parent
f224ca1461
commit
69f87ca075
|
@ -205,6 +205,7 @@ export class MetadataCollector {
|
|||
switch (node.kind) {
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
const classDeclaration = <ts.ClassDeclaration>node;
|
||||
if (classDeclaration.name) {
|
||||
const className = classDeclaration.name.text;
|
||||
if (node.flags & ts.NodeFlags.Export) {
|
||||
locals.define(className, {__symbolic: 'reference', name: className});
|
||||
|
@ -212,15 +213,19 @@ export class MetadataCollector {
|
|||
locals.define(
|
||||
className, errorSym('Reference to non-exported class', node, {className}));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case ts.SyntaxKind.FunctionDeclaration:
|
||||
if (!(node.flags & ts.NodeFlags.Export)) {
|
||||
// Report references to this function as an error.
|
||||
const functionDeclaration = <ts.FunctionDeclaration>node;
|
||||
const nameNode = functionDeclaration.name;
|
||||
if (nameNode && nameNode.text) {
|
||||
locals.define(
|
||||
nameNode.text,
|
||||
errorSym('Reference to a non-exported function', nameNode, {name: nameNode.text}));
|
||||
errorSym(
|
||||
'Reference to a non-exported function', nameNode, {name: nameNode.text}));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -248,6 +253,7 @@ export class MetadataCollector {
|
|||
break;
|
||||
case ts.SyntaxKind.ClassDeclaration:
|
||||
const classDeclaration = <ts.ClassDeclaration>node;
|
||||
if (classDeclaration.name) {
|
||||
const className = classDeclaration.name.text;
|
||||
if (node.flags & ts.NodeFlags.Export) {
|
||||
if (classDeclaration.decorators) {
|
||||
|
@ -255,6 +261,7 @@ export class MetadataCollector {
|
|||
metadata[className] = classMetadataOf(classDeclaration);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Otherwise don't record metadata for the class.
|
||||
break;
|
||||
case ts.SyntaxKind.FunctionDeclaration:
|
||||
|
|
|
@ -15,7 +15,7 @@ import {Directory, Host, expectValidSources} from './typescript.mocks';
|
|||
|
||||
describe('Collector', () => {
|
||||
let documentRegistry = ts.createDocumentRegistry();
|
||||
let host: ts.LanguageServiceHost;
|
||||
let host: Host;
|
||||
let service: ts.LanguageService;
|
||||
let program: ts.Program;
|
||||
let collector: MetadataCollector;
|
||||
|
@ -589,6 +589,28 @@ describe('Collector', () => {
|
|||
.toThrowError(/Reference to non-exported class/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with invalid input', () => {
|
||||
it('should not throw with a class with no name', () => {
|
||||
const fileName = '/invalid-class.ts';
|
||||
override(fileName, 'export class');
|
||||
let invalidClass = program.getSourceFile(fileName);
|
||||
expect(() => collector.getMetadata(invalidClass)).not.toThrow();
|
||||
});
|
||||
|
||||
it('should not throw with a function with no name', () => {
|
||||
const fileName = '/invalid-function.ts';
|
||||
override(fileName, 'export function');
|
||||
let invalidFunction = program.getSourceFile(fileName);
|
||||
expect(() => collector.getMetadata(invalidFunction)).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
function override(fileName: string, content: string) {
|
||||
host.overrideFile(fileName, content);
|
||||
host.addFile(fileName);
|
||||
program = service.getProgram();
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Do not use \` in a template literal as it confuses clang-format
|
||||
|
|
|
@ -5,6 +5,9 @@ import * as ts from 'typescript';
|
|||
export interface Directory { [name: string]: (Directory|string); }
|
||||
|
||||
export class Host implements ts.LanguageServiceHost {
|
||||
private overrides = new Map<string, string>();
|
||||
private version = 1;
|
||||
|
||||
constructor(private directory: Directory, private scripts: string[]) {}
|
||||
|
||||
getCompilationSettings(): ts.CompilerOptions {
|
||||
|
@ -17,7 +20,7 @@ export class Host implements ts.LanguageServiceHost {
|
|||
|
||||
getScriptFileNames(): string[] { return this.scripts; }
|
||||
|
||||
getScriptVersion(fileName: string): string { return '1'; }
|
||||
getScriptVersion(fileName: string): string { return this.version.toString(); }
|
||||
|
||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
||||
let content = this.getFileContent(fileName);
|
||||
|
@ -28,7 +31,20 @@ export class Host implements ts.LanguageServiceHost {
|
|||
|
||||
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; }
|
||||
|
||||
overrideFile(fileName: string, content: string) {
|
||||
this.overrides.set(fileName, content);
|
||||
this.version++;
|
||||
}
|
||||
|
||||
addFile(fileName: string) {
|
||||
this.scripts.push(fileName);
|
||||
this.version++;
|
||||
}
|
||||
|
||||
private getFileContent(fileName: string): string {
|
||||
if (this.overrides.has(fileName)) {
|
||||
return this.overrides.get(fileName);
|
||||
}
|
||||
const names = fileName.split('/');
|
||||
if (names[names.length - 1] === 'lib.d.ts') {
|
||||
return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8');
|
||||
|
|
Loading…
Reference in New Issue