fix(tools): harden colletor against invalid asts (#12793)
This commit is contained in:
parent
f224ca1461
commit
69f87ca075
|
@ -205,12 +205,14 @@ export class MetadataCollector {
|
||||||
switch (node.kind) {
|
switch (node.kind) {
|
||||||
case ts.SyntaxKind.ClassDeclaration:
|
case ts.SyntaxKind.ClassDeclaration:
|
||||||
const classDeclaration = <ts.ClassDeclaration>node;
|
const classDeclaration = <ts.ClassDeclaration>node;
|
||||||
const className = classDeclaration.name.text;
|
if (classDeclaration.name) {
|
||||||
if (node.flags & ts.NodeFlags.Export) {
|
const className = classDeclaration.name.text;
|
||||||
locals.define(className, {__symbolic: 'reference', name: className});
|
if (node.flags & ts.NodeFlags.Export) {
|
||||||
} else {
|
locals.define(className, {__symbolic: 'reference', name: className});
|
||||||
locals.define(
|
} else {
|
||||||
className, errorSym('Reference to non-exported class', node, {className}));
|
locals.define(
|
||||||
|
className, errorSym('Reference to non-exported class', node, {className}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ts.SyntaxKind.FunctionDeclaration:
|
case ts.SyntaxKind.FunctionDeclaration:
|
||||||
|
@ -218,9 +220,12 @@ export class MetadataCollector {
|
||||||
// Report references to this function as an error.
|
// Report references to this function as an error.
|
||||||
const functionDeclaration = <ts.FunctionDeclaration>node;
|
const functionDeclaration = <ts.FunctionDeclaration>node;
|
||||||
const nameNode = functionDeclaration.name;
|
const nameNode = functionDeclaration.name;
|
||||||
locals.define(
|
if (nameNode && nameNode.text) {
|
||||||
nameNode.text,
|
locals.define(
|
||||||
errorSym('Reference to a non-exported function', nameNode, {name: nameNode.text}));
|
nameNode.text,
|
||||||
|
errorSym(
|
||||||
|
'Reference to a non-exported function', nameNode, {name: nameNode.text}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -248,11 +253,13 @@ export class MetadataCollector {
|
||||||
break;
|
break;
|
||||||
case ts.SyntaxKind.ClassDeclaration:
|
case ts.SyntaxKind.ClassDeclaration:
|
||||||
const classDeclaration = <ts.ClassDeclaration>node;
|
const classDeclaration = <ts.ClassDeclaration>node;
|
||||||
const className = classDeclaration.name.text;
|
if (classDeclaration.name) {
|
||||||
if (node.flags & ts.NodeFlags.Export) {
|
const className = classDeclaration.name.text;
|
||||||
if (classDeclaration.decorators) {
|
if (node.flags & ts.NodeFlags.Export) {
|
||||||
if (!metadata) metadata = {};
|
if (classDeclaration.decorators) {
|
||||||
metadata[className] = classMetadataOf(classDeclaration);
|
if (!metadata) metadata = {};
|
||||||
|
metadata[className] = classMetadataOf(classDeclaration);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Otherwise don't record metadata for the class.
|
// Otherwise don't record metadata for the class.
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {Directory, Host, expectValidSources} from './typescript.mocks';
|
||||||
|
|
||||||
describe('Collector', () => {
|
describe('Collector', () => {
|
||||||
let documentRegistry = ts.createDocumentRegistry();
|
let documentRegistry = ts.createDocumentRegistry();
|
||||||
let host: ts.LanguageServiceHost;
|
let host: Host;
|
||||||
let service: ts.LanguageService;
|
let service: ts.LanguageService;
|
||||||
let program: ts.Program;
|
let program: ts.Program;
|
||||||
let collector: MetadataCollector;
|
let collector: MetadataCollector;
|
||||||
|
@ -589,6 +589,28 @@ describe('Collector', () => {
|
||||||
.toThrowError(/Reference to non-exported class/);
|
.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
|
// 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 interface Directory { [name: string]: (Directory|string); }
|
||||||
|
|
||||||
export class Host implements ts.LanguageServiceHost {
|
export class Host implements ts.LanguageServiceHost {
|
||||||
|
private overrides = new Map<string, string>();
|
||||||
|
private version = 1;
|
||||||
|
|
||||||
constructor(private directory: Directory, private scripts: string[]) {}
|
constructor(private directory: Directory, private scripts: string[]) {}
|
||||||
|
|
||||||
getCompilationSettings(): ts.CompilerOptions {
|
getCompilationSettings(): ts.CompilerOptions {
|
||||||
|
@ -17,7 +20,7 @@ export class Host implements ts.LanguageServiceHost {
|
||||||
|
|
||||||
getScriptFileNames(): string[] { return this.scripts; }
|
getScriptFileNames(): string[] { return this.scripts; }
|
||||||
|
|
||||||
getScriptVersion(fileName: string): string { return '1'; }
|
getScriptVersion(fileName: string): string { return this.version.toString(); }
|
||||||
|
|
||||||
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
|
||||||
let content = this.getFileContent(fileName);
|
let content = this.getFileContent(fileName);
|
||||||
|
@ -28,7 +31,20 @@ export class Host implements ts.LanguageServiceHost {
|
||||||
|
|
||||||
getDefaultLibFileName(options: ts.CompilerOptions): string { return 'lib.d.ts'; }
|
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 {
|
private getFileContent(fileName: string): string {
|
||||||
|
if (this.overrides.has(fileName)) {
|
||||||
|
return this.overrides.get(fileName);
|
||||||
|
}
|
||||||
const names = fileName.split('/');
|
const names = fileName.split('/');
|
||||||
if (names[names.length - 1] === 'lib.d.ts') {
|
if (names[names.length - 1] === 'lib.d.ts') {
|
||||||
return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8');
|
return fs.readFileSync(ts.getDefaultLibFilePath(this.getCompilationSettings()), 'utf8');
|
||||||
|
|
Loading…
Reference in New Issue