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

@ -13,7 +13,7 @@ import {Diagnostics} from '../src/types';
import {TypeScriptServiceHost} from '../src/typescript_host';
import {toh} from './test_data';
import {MockTypescriptHost, includeDiagnostic, noDiagnostics} from './test_utils';
import {MockTypescriptHost, diagnosticMessageContains, findDiagnostic, includeDiagnostic, noDiagnostics} from './test_utils';
describe('diagnostics', () => {
let documentRegistry = ts.createDocumentRegistry();
@ -123,7 +123,8 @@ describe('diagnostics', () => {
addCode(code, (fileName, content) => {
const diagnostics = ngService.getDiagnostics(fileName);
includeDiagnostic(
diagnostics !, 'Function calls are not supported.', '() => \'foo\'', content);
diagnostics !, 'Function expressions are not supported in decorators', '() => \'foo\'',
content);
});
});
@ -168,8 +169,7 @@ describe('diagnostics', () => {
const code =
` @Component({template: '<p> Using an invalid pipe {{data | dat}} </p>'}) export class MyComponent { data = 'some data'; }`;
addCode(code, fileName => {
const diagnostic =
ngService.getDiagnostics(fileName) !.filter(d => d.message.indexOf('pipe') > 0)[0];
const diagnostic = findDiagnostic(ngService.getDiagnostics(fileName) !, 'pipe') !;
expect(diagnostic).not.toBeUndefined();
expect(diagnostic.span.end - diagnostic.span.start).toBeLessThan(11);
});
@ -216,8 +216,8 @@ describe('diagnostics', () => {
`,
fileName => {
const diagnostics = ngService.getDiagnostics(fileName) !;
const expected = diagnostics.find(d => d.message.startsWith('Invalid providers for'));
const notExpected = diagnostics.find(d => d.message.startsWith('Cannot read property'));
const expected = findDiagnostic(diagnostics, 'Invalid providers for');
const notExpected = findDiagnostic(diagnostics, 'Cannot read property');
expect(expected).toBeDefined();
expect(notExpected).toBeUndefined();
});
@ -355,12 +355,12 @@ describe('diagnostics', () => {
expect(diagnostics.length).toBe(1);
if (diagnostics.length > 1) {
for (const diagnostic of diagnostics) {
if (diagnostic.message.indexOf('MyComponent') >= 0) continue;
if (diagnosticMessageContains(diagnostic.message, 'MyComponent')) continue;
fail(`(${diagnostic.span.start}:${diagnostic.span.end}): ${diagnostic.message}`);
}
return;
}
expect(diagnostics[0].message.indexOf('MyComponent') >= 0).toBeTruthy();
expect(diagnosticMessageContains(diagnostics[0].message, 'MyComponent')).toBeTruthy();
}
});
});

View File

@ -10,7 +10,7 @@ import * as fs from 'fs';
import * as path from 'path';
import * as ts from 'typescript';
import {Diagnostic, Diagnostics, Span} from '../src/types';
import {Diagnostic, DiagnosticMessageChain, Diagnostics, Span} from '../src/types';
export type MockData = string | MockDirectory;
@ -317,6 +317,25 @@ export function noDiagnostics(diagnostics: Diagnostics) {
}
}
export function diagnosticMessageContains(
message: string | DiagnosticMessageChain, messageFragment: string): boolean {
if (typeof message == 'string') {
return message.indexOf(messageFragment) >= 0;
}
if (message.message.indexOf(messageFragment) >= 0) {
return true;
}
if (message.next) {
return diagnosticMessageContains(message.next, messageFragment);
}
return false;
}
export function findDiagnostic(diagnostics: Diagnostic[], messageFragment: string): Diagnostic|
undefined {
return diagnostics.find(d => diagnosticMessageContains(d.message, messageFragment));
}
export function includeDiagnostic(
diagnostics: Diagnostics, message: string, text?: string, len?: string): void;
export function includeDiagnostic(
@ -324,14 +343,18 @@ export function includeDiagnostic(
export function includeDiagnostic(diagnostics: Diagnostics, message: string, p1?: any, p2?: any) {
expect(diagnostics).toBeDefined();
if (diagnostics) {
const diagnostic = diagnostics.find(d => d.message.indexOf(message) >= 0) as Diagnostic;
expect(diagnostic).toBeDefined();
const diagnostic = findDiagnostic(diagnostics, message);
expect(diagnostic).toBeDefined(`no diagnostic contains '${message}`);
if (diagnostic && p1 != null) {
const at = typeof p1 === 'number' ? p1 : p2.indexOf(p1);
const len = typeof p2 === 'number' ? p2 : p1.length;
expect(diagnostic.span.start).toEqual(at);
expect(diagnostic.span.start)
.toEqual(
at,
`expected message '${message}' was reported at ${diagnostic.span.start} but should be ${at}`);
if (len != null) {
expect(diagnostic.span.end - diagnostic.span.start).toEqual(len);
expect(diagnostic.span.end - diagnostic.span.start)
.toEqual(len, `expected '${message}'s span length to be ${len}`);
}
}
}