fix(ivy): template type-check errors from TS should not use NG error codes (#35146)

A bug previously caused the template type-checking diagnostics produced by
TypeScript for template expressions to use -99-prefixed error codes. These
codes are converted to "NG" errors instead of "TS" errors during diagnostic
printing. This commit fixes the issue.

PR Close #35146
This commit is contained in:
Alex Rickabaugh 2020-02-04 12:23:54 -08:00 committed by Miško Hevery
parent 1f6da8c7d5
commit c35671c0a4
4 changed files with 17 additions and 11 deletions

View File

@ -8,7 +8,6 @@
import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler'; import {AbsoluteSourceSpan, ParseSourceSpan} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {getTokenAtPosition} from '../../util/src/typescript'; import {getTokenAtPosition} from '../../util/src/typescript';
import {ExternalTemplateSourceMapping, TemplateId, TemplateSourceMapping} from './api'; import {ExternalTemplateSourceMapping, TemplateId, TemplateSourceMapping} from './api';
@ -146,7 +145,7 @@ export function translateDiagnostic(
*/ */
export function makeTemplateDiagnostic( export function makeTemplateDiagnostic(
mapping: TemplateSourceMapping, span: ParseSourceSpan, category: ts.DiagnosticCategory, mapping: TemplateSourceMapping, span: ParseSourceSpan, category: ts.DiagnosticCategory,
code: ErrorCode, messageText: string | ts.DiagnosticMessageChain, relatedMessage?: { code: number, messageText: string | ts.DiagnosticMessageChain, relatedMessage?: {
text: string, text: string,
span: ParseSourceSpan, span: ParseSourceSpan,
}): TemplateDiagnostic { }): TemplateDiagnostic {
@ -167,7 +166,9 @@ export function makeTemplateDiagnostic(
// directly into the bytes of the source file. // directly into the bytes of the source file.
return { return {
source: 'ngtsc', source: 'ngtsc',
code: ngErrorCode(code), category, messageText, code,
category,
messageText,
file: mapping.node.getSourceFile(), file: mapping.node.getSourceFile(),
componentFile: mapping.node.getSourceFile(), componentFile: mapping.node.getSourceFile(),
start: span.start.offset, start: span.start.offset,
@ -216,7 +217,8 @@ export function makeTemplateDiagnostic(
return { return {
source: 'ngtsc', source: 'ngtsc',
category, category,
code: ngErrorCode(code), messageText, code,
messageText,
file: sf, file: sf,
componentFile: componentSf, componentFile: componentSf,
start: span.start.offset, start: span.start.offset,

View File

@ -9,7 +9,7 @@
import {DomElementSchemaRegistry, ParseSourceSpan, SchemaMetadata, TmplAstElement} from '@angular/compiler'; import {DomElementSchemaRegistry, ParseSourceSpan, SchemaMetadata, TmplAstElement} from '@angular/compiler';
import * as ts from 'typescript'; import * as ts from 'typescript';
import {ErrorCode} from '../../diagnostics'; import {ErrorCode, ngErrorCode} from '../../diagnostics';
import {TemplateId} from './api'; import {TemplateId} from './api';
import {TemplateSourceResolver, makeTemplateDiagnostic} from './diagnostics'; import {TemplateSourceResolver, makeTemplateDiagnostic} from './diagnostics';
@ -92,7 +92,7 @@ export class RegistryDomSchemaChecker implements DomSchemaChecker {
const diag = makeTemplateDiagnostic( const diag = makeTemplateDiagnostic(
mapping, element.sourceSpan, ts.DiagnosticCategory.Error, mapping, element.sourceSpan, ts.DiagnosticCategory.Error,
ErrorCode.SCHEMA_INVALID_ELEMENT, errorMsg); ngErrorCode(ErrorCode.SCHEMA_INVALID_ELEMENT), errorMsg);
this._diagnostics.push(diag); this._diagnostics.push(diag);
} }
} }
@ -117,7 +117,8 @@ export class RegistryDomSchemaChecker implements DomSchemaChecker {
} }
const diag = makeTemplateDiagnostic( const diag = makeTemplateDiagnostic(
mapping, span, ts.DiagnosticCategory.Error, ErrorCode.SCHEMA_INVALID_ATTRIBUTE, errorMsg); mapping, span, ts.DiagnosticCategory.Error,
ngErrorCode(ErrorCode.SCHEMA_INVALID_ATTRIBUTE), errorMsg);
this._diagnostics.push(diag); this._diagnostics.push(diag);
} }
} }

View File

@ -66,7 +66,7 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
const errorMsg = `No directive found with exportAs '${value}'.`; const errorMsg = `No directive found with exportAs '${value}'.`;
this._diagnostics.push(makeTemplateDiagnostic( this._diagnostics.push(makeTemplateDiagnostic(
mapping, ref.valueSpan || ref.sourceSpan, ts.DiagnosticCategory.Error, mapping, ref.valueSpan || ref.sourceSpan, ts.DiagnosticCategory.Error,
ErrorCode.MISSING_REFERENCE_TARGET, errorMsg)); ngErrorCode(ErrorCode.MISSING_REFERENCE_TARGET), errorMsg));
} }
missingPipe(templateId: TemplateId, ast: BindingPipe): void { missingPipe(templateId: TemplateId, ast: BindingPipe): void {
@ -79,7 +79,8 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
`Assertion failure: no SourceLocation found for usage of pipe '${ast.name}'.`); `Assertion failure: no SourceLocation found for usage of pipe '${ast.name}'.`);
} }
this._diagnostics.push(makeTemplateDiagnostic( this._diagnostics.push(makeTemplateDiagnostic(
mapping, sourceSpan, ts.DiagnosticCategory.Error, ErrorCode.MISSING_PIPE, errorMsg)); mapping, sourceSpan, ts.DiagnosticCategory.Error, ngErrorCode(ErrorCode.MISSING_PIPE),
errorMsg));
} }
illegalAssignmentToTemplateVar( illegalAssignmentToTemplateVar(
@ -93,8 +94,8 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
throw new Error(`Assertion failure: no SourceLocation found for property binding.`); throw new Error(`Assertion failure: no SourceLocation found for property binding.`);
} }
this._diagnostics.push(makeTemplateDiagnostic( this._diagnostics.push(makeTemplateDiagnostic(
mapping, sourceSpan, ts.DiagnosticCategory.Error, ErrorCode.WRITE_TO_READ_ONLY_VARIABLE, mapping, sourceSpan, ts.DiagnosticCategory.Error,
errorMsg, { ngErrorCode(ErrorCode.WRITE_TO_READ_ONLY_VARIABLE), errorMsg, {
text: `The variable ${assignment.name} is declared here.`, text: `The variable ${assignment.name} is declared here.`,
span: target.valueSpan || target.sourceSpan, span: target.valueSpan || target.sourceSpan,
})); }));

View File

@ -135,6 +135,8 @@ export declare class AnimationEvent {
const diags = env.driveDiagnostics(); const diags = env.driveDiagnostics();
expect(diags.length).toBe(1); expect(diags.length).toBe(1);
expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`); expect(diags[0].messageText).toEqual(`Type 'string' is not assignable to type 'number'.`);
// The reported error code should be in the TS error space, not a -99 "NG" code.
expect(diags[0].code).toBeGreaterThan(0);
}); });
it('should support inputs and outputs with names that are not JavaScript identifiers', () => { it('should support inputs and outputs with names that are not JavaScript identifiers', () => {