feat(compiler-cli): improve error messages produced during structural errors (#20459)

The errors produced when error were encountered while interpreting the
content of a directive was often incomprehencible. With this change
these kind of error messages should be easier to understand and diagnose.

PR Close #20459
This commit is contained in:
Chuck Jazdzewski
2017-11-14 17:49:47 -08:00
committed by Miško Hevery
parent 1366762d12
commit 8ecda94899
25 changed files with 1247 additions and 308 deletions

View File

@ -9,10 +9,11 @@
import * as ts from 'typescript';
import {CollectorOptions} from './collector';
import {MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
import {ClassMetadata, FunctionMetadata, InterfaceMetadata, MetadataEntry, MetadataError, MetadataImportedSymbolReferenceExpression, MetadataSourceLocationInfo, MetadataSymbolicCallExpression, MetadataValue, isMetadataError, isMetadataGlobalReferenceExpression, isMetadataImportDefaultReference, isMetadataImportedSymbolReferenceExpression, isMetadataModuleReferenceExpression, isMetadataSymbolicReferenceExpression, isMetadataSymbolicSpreadExpression} from './schema';
import {Symbols} from './symbols';
// In TypeScript 2.1 the spread element kind was renamed.
const spreadElementSyntaxKind: ts.SyntaxKind =
(ts.SyntaxKind as any).SpreadElement || (ts.SyntaxKind as any).SpreadElementExpression;
@ -38,6 +39,24 @@ function isCallOf(callExpression: ts.CallExpression, ident: string): boolean {
return false;
}
/* @internal */
export function recordMapEntry<T extends MetadataEntry>(
entry: T, node: ts.Node,
nodeMap: Map<MetadataValue|ClassMetadata|InterfaceMetadata|FunctionMetadata, ts.Node>,
sourceFile?: ts.SourceFile) {
if (!nodeMap.has(entry)) {
nodeMap.set(entry, node);
if (node && (isMetadataImportedSymbolReferenceExpression(entry) ||
isMetadataImportDefaultReference(entry)) &&
entry.line == null) {
const info = sourceInfo(node, sourceFile);
if (info.line != null) entry.line = info.line;
if (info.character != null) entry.character = info.character;
}
}
return entry;
}
/**
* ts.forEachChild stops iterating children when the callback return a truthy value.
* This method inverts this to implement an `every` style iterator. It will return
@ -77,21 +96,22 @@ function getSourceFileOfNode(node: ts.Node | undefined): ts.SourceFile {
}
/* @internal */
export function errorSymbol(
message: string, node?: ts.Node, context?: {[name: string]: string},
sourceFile?: ts.SourceFile): MetadataError {
let result: MetadataError|undefined = undefined;
export function sourceInfo(
node: ts.Node | undefined, sourceFile: ts.SourceFile | undefined): MetadataSourceLocationInfo {
if (node) {
sourceFile = sourceFile || getSourceFileOfNode(node);
if (sourceFile) {
const {line, character} =
ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
result = {__symbolic: 'error', message, line, character};
return ts.getLineAndCharacterOfPosition(sourceFile, node.getStart(sourceFile));
}
}
if (!result) {
result = {__symbolic: 'error', message};
}
return {};
}
/* @internal */
export function errorSymbol(
message: string, node?: ts.Node, context?: {[name: string]: string},
sourceFile?: ts.SourceFile): MetadataError {
const result: MetadataError = {__symbolic: 'error', message, ...sourceInfo(node, sourceFile)};
if (context) {
result.context = context;
}
@ -242,8 +262,7 @@ export class Evaluator {
}
entry = newEntry;
}
t.nodeMap.set(entry, node);
return entry;
return recordMapEntry(entry, node, t.nodeMap);
}
function isFoldableError(value: any): value is MetadataError {
@ -256,6 +275,9 @@ export class Evaluator {
// Encode as a global reference. StaticReflector will check the reference.
return recordEntry({__symbolic: 'reference', name}, node);
}
if (reference && isMetadataSymbolicReferenceExpression(reference)) {
return recordEntry({...reference}, node);
}
return reference;
};
@ -628,7 +650,7 @@ export class Evaluator {
return recordEntry({__symbolic: 'if', condition, thenExpression, elseExpression}, node);
case ts.SyntaxKind.FunctionExpression:
case ts.SyntaxKind.ArrowFunction:
return recordEntry(errorSymbol('Function call not supported', node), node);
return recordEntry(errorSymbol('Lambda not supported', node), node);
case ts.SyntaxKind.TaggedTemplateExpression:
return recordEntry(
errorSymbol('Tagged template expressions are not supported in metadata', node), node);