fix(compiler): Ignore references to declared modules and unneeded types (#9776)

Fixes: #9670
This commit is contained in:
Chuck Jazdzewski
2016-07-11 17:26:35 -07:00
committed by GitHub
parent eb5763c23f
commit 4ef86891a3
7 changed files with 70 additions and 46 deletions

View File

@ -1,10 +1,11 @@
import * as ts from 'typescript';
import {Evaluator, errorSymbol, isPrimitive} from './evaluator';
import {ClassMetadata, ConstructorMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression} from './schema';
import {ClassMetadata, ConstructorMetadata, MemberMetadata, MetadataError, MetadataMap, MetadataSymbolicExpression, MetadataSymbolicReferenceExpression, MetadataSymbolicSelectExpression, MetadataValue, MethodMetadata, ModuleMetadata, VERSION, isMetadataError, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSelectExpression} from './schema';
import {Symbols} from './symbols';
/**
* Collect decorator metadata from a TypeScript module.
*/
@ -38,9 +39,11 @@ export class MetadataCollector {
return undefined;
}
function referenceFrom(node: ts.Node): MetadataSymbolicReferenceExpression|MetadataError {
function referenceFrom(node: ts.Node): MetadataSymbolicReferenceExpression|MetadataError|
MetadataSymbolicSelectExpression {
const result = evaluator.evaluateNode(node);
if (isMetadataError(result) || isMetadataSymbolicReferenceExpression(result)) {
if (isMetadataError(result) || isMetadataSymbolicReferenceExpression(result) ||
isMetadataSymbolicSelectExpression(result)) {
return result;
} else {
return errorSym('Symbol reference expected', node);
@ -70,8 +73,9 @@ export class MetadataCollector {
const methodDecorators = getDecorators(method.decorators);
const parameters = method.parameters;
const parameterDecoratorData: (MetadataSymbolicExpression | MetadataError)[][] = [];
const parametersData: (MetadataSymbolicReferenceExpression | MetadataError | null)[] =
[];
const parametersData:
(MetadataSymbolicReferenceExpression | MetadataError |
MetadataSymbolicSelectExpression | null)[] = [];
let hasDecoratorData: boolean = false;
let hasParameterData: boolean = false;
for (const parameter of parameters) {

View File

@ -354,8 +354,8 @@ export class Evaluator {
case ts.SyntaxKind.TypeReference:
const typeReferenceNode = <ts.TypeReferenceNode>node;
const typeNameNode = typeReferenceNode.typeName;
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) =>
MetadataSymbolicReferenceExpression | MetadataError = node => {
const getReference: (typeNameNode: ts.Identifier | ts.QualifiedName) => MetadataValue =
node => {
if (typeNameNode.kind === ts.SyntaxKind.QualifiedName) {
const qualifiedName = <ts.QualifiedName>node;
const left = this.evaluateNode(qualifiedName.left);
@ -364,7 +364,8 @@ export class Evaluator {
__symbolic: 'reference', module: left.module, name: qualifiedName.right.text
}
}
return errorSymbol('Qualified type names not supported', node);
// Record a type reference to a declared type as a select.
return {__symbolic: 'select', expression: left, member: qualifiedName.right.text};
} else {
const identifier = <ts.Identifier>typeNameNode;
let symbol = this.symbols.resolve(identifier.text);

View File

@ -18,7 +18,7 @@ describe('Evaluator', () => {
beforeEach(() => {
host = new Host(FILES, [
'expressions.ts', 'consts.ts', 'const_expr.ts', 'forwardRef.ts', 'classes.ts',
'newExpression.ts', 'errors.ts'
'newExpression.ts', 'errors.ts', 'declared.ts'
]);
service = ts.createLanguageService(host, documentRegistry);
program = service.getProgram();
@ -39,7 +39,7 @@ describe('Evaluator', () => {
});
it('should be able to fold literal expressions', () => {
var consts = program.getSourceFile('consts.ts');
const consts = program.getSourceFile('consts.ts');
expect(evaluator.isFoldable(findVar(consts, 'someName').initializer)).toBeTruthy();
expect(evaluator.isFoldable(findVar(consts, 'someBool').initializer)).toBeTruthy();
expect(evaluator.isFoldable(findVar(consts, 'one').initializer)).toBeTruthy();
@ -47,7 +47,7 @@ describe('Evaluator', () => {
});
it('should be able to fold expressions with foldable references', () => {
var expressions = program.getSourceFile('expressions.ts');
const expressions = program.getSourceFile('expressions.ts');
symbols.define('someName', 'some-name');
symbols.define('someBool', true);
symbols.define('one', 1);
@ -61,7 +61,7 @@ describe('Evaluator', () => {
});
it('should be able to evaluate literal expressions', () => {
var consts = program.getSourceFile('consts.ts');
const consts = program.getSourceFile('consts.ts');
expect(evaluator.evaluateNode(findVar(consts, 'someName').initializer)).toBe('some-name');
expect(evaluator.evaluateNode(findVar(consts, 'someBool').initializer)).toBe(true);
expect(evaluator.evaluateNode(findVar(consts, 'one').initializer)).toBe(1);
@ -69,7 +69,7 @@ describe('Evaluator', () => {
});
it('should be able to evaluate expressions', () => {
var expressions = program.getSourceFile('expressions.ts');
const expressions = program.getSourceFile('expressions.ts');
symbols.define('someName', 'some-name');
symbols.define('someBool', true);
symbols.define('one', 1);
@ -117,7 +117,7 @@ describe('Evaluator', () => {
});
it('should report recursive references as symbolic', () => {
var expressions = program.getSourceFile('expressions.ts');
const expressions = program.getSourceFile('expressions.ts');
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveA').initializer))
.toEqual({__symbolic: 'reference', name: 'recursiveB'});
expect(evaluator.evaluateNode(findVar(expressions, 'recursiveB').initializer))
@ -125,13 +125,13 @@ describe('Evaluator', () => {
});
it('should correctly handle special cases for CONST_EXPR', () => {
var const_expr = program.getSourceFile('const_expr.ts');
const const_expr = program.getSourceFile('const_expr.ts');
expect(evaluator.evaluateNode(findVar(const_expr, 'bTrue').initializer)).toEqual(true);
expect(evaluator.evaluateNode(findVar(const_expr, 'bFalse').initializer)).toEqual(false);
});
it('should resolve a forwardRef', () => {
var forwardRef = program.getSourceFile('forwardRef.ts');
const forwardRef = program.getSourceFile('forwardRef.ts');
expect(evaluator.evaluateNode(findVar(forwardRef, 'bTrue').initializer)).toEqual(true);
expect(evaluator.evaluateNode(findVar(forwardRef, 'bFalse').initializer)).toEqual(false);
});
@ -139,7 +139,7 @@ describe('Evaluator', () => {
it('should return new expressions', () => {
symbols.define('Value', {__symbolic: 'reference', module: './classes', name: 'Value'});
evaluator = new Evaluator(symbols);
var newExpression = program.getSourceFile('newExpression.ts');
const newExpression = program.getSourceFile('newExpression.ts');
expect(evaluator.evaluateNode(findVar(newExpression, 'someValue').initializer)).toEqual({
__symbolic: 'new',
expression: {__symbolic: 'reference', name: 'Value', module: './classes'},
@ -152,54 +152,57 @@ describe('Evaluator', () => {
});
});
it('should return errors for unsupported expressions', () => {
let errors = program.getSourceFile('errors.ts');
let aDecl = findVar(errors, 'a');
it('should support referene to a declared module type', () => {
const declared = program.getSourceFile('declared.ts');
const aDecl = findVar(declared, 'a');
expect(evaluator.evaluateNode(aDecl.type)).toEqual({
__symbolic: 'error',
message: 'Qualified type names not supported',
line: 5,
character: 10
__symbolic: 'select',
expression: {__symbolic: 'reference', name: 'Foo'},
member: 'A'
});
let fDecl = findVar(errors, 'f');
});
it('should return errors for unsupported expressions', () => {
const errors = program.getSourceFile('errors.ts');
const fDecl = findVar(errors, 'f');
expect(evaluator.evaluateNode(fDecl.initializer))
.toEqual(
{__symbolic: 'error', message: 'Function call not supported', line: 6, character: 11});
let eDecl = findVar(errors, 'e');
{__symbolic: 'error', message: 'Function call not supported', line: 1, character: 11});
const eDecl = findVar(errors, 'e');
expect(evaluator.evaluateNode(eDecl.type)).toEqual({
__symbolic: 'error',
message: 'Could not resolve type',
line: 7,
line: 2,
character: 10,
context: {typeName: 'NotFound'}
});
let sDecl = findVar(errors, 's');
const sDecl = findVar(errors, 's');
expect(evaluator.evaluateNode(sDecl.initializer)).toEqual({
__symbolic: 'error',
message: 'Name expected',
line: 8,
line: 3,
character: 13,
context: {received: '1'}
});
let tDecl = findVar(errors, 't');
const tDecl = findVar(errors, 't');
expect(evaluator.evaluateNode(tDecl.initializer)).toEqual({
__symbolic: 'error',
message: 'Expression form not supported',
line: 9,
line: 4,
character: 11
});
});
it('should be able to fold an array spread', () => {
let expressions = program.getSourceFile('expressions.ts');
const expressions = program.getSourceFile('expressions.ts');
symbols.define('arr', [1, 2, 3, 4]);
let arrSpread = findVar(expressions, 'arrSpread');
const arrSpread = findVar(expressions, 'arrSpread');
expect(evaluator.evaluateNode(arrSpread.initializer)).toEqual([0, 1, 2, 3, 4, 5]);
});
it('should be able to produce a spread expression', () => {
let expressions = program.getSourceFile('expressions.ts');
let arrSpreadRef = findVar(expressions, 'arrSpreadRef');
const expressions = program.getSourceFile('expressions.ts');
const arrSpreadRef = findVar(expressions, 'arrSpreadRef');
expect(evaluator.evaluateNode(arrSpreadRef.initializer)).toEqual([
0, {__symbolic: 'spread', expression: {__symbolic: 'reference', name: 'arrImport'}}, 5
]);
@ -296,14 +299,16 @@ const FILES: Directory = {
export const complex = CONST_EXPR(new Value("name", forwardRef(() => 12)));
`,
'errors.ts': `
declare namespace Foo {
type A = string;
}
let a: Foo.A = 'some value';
let f = () => 1;
let e: NotFound;
let s = { 1: 1, 2: 2 };
let t = typeof 12;
`,
'declared.ts': `
declare namespace Foo {
type A = string;
}
let a: Foo.A = 'some value';
`
};