From 67930ead812893043b7a45888053a2a9bc2cf313 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 1 Mar 2019 14:48:39 -0800 Subject: [PATCH] Store semantic diagnostics as well (for future tsc without build but incremental) --- src/compiler/builder.ts | 118 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 112 insertions(+), 6 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 0872763c6ce..b3ab501546e 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1,10 +1,33 @@ /*@internal*/ namespace ts { + export interface ReusableDiagnostic extends ReusableDiagnosticRelatedInformation { + /** May store more in future. For now, this will simply be `true` to indicate when a diagnostic is an unused-identifier diagnostic. */ + reportsUnnecessary?: {}; + source?: string; + relatedInformation?: ReusableDiagnosticRelatedInformation[]; + } + + export interface ReusableDiagnosticRelatedInformation { + category: DiagnosticCategory; + code: number; + file: Path | undefined; + start: number | undefined; + length: number | undefined; + messageText: string | ReusableDiagnosticMessageChain; + } + + export interface ReusableDiagnosticMessageChain { + messageText: string; + category: DiagnosticCategory; + code: number; + next?: ReusableDiagnosticMessageChain; + } + export interface ReusableBuilderProgramState extends ReusableBuilderState { /** * Cache of semantic diagnostics for files with their Path being the key */ - semanticDiagnosticsPerFile?: ReadonlyMap> | undefined; + semanticDiagnosticsPerFile?: ReadonlyMap | ReadonlyArray> | undefined; /** * The map has key by source file's path that has been changed */ @@ -46,6 +69,10 @@ namespace ts { * Current index to retrieve pending affected file */ affectedFilesPendingEmitIndex?: number | undefined; + /* + * true if semantic diagnostics are ReusableDiagnostic instead of Diagnostic + */ + hasReusableDiagnostic?: true; } /** @@ -200,7 +227,7 @@ namespace ts { // Unchanged file copy diagnostics const diagnostics = oldState!.semanticDiagnosticsPerFile!.get(sourceFilePath); if (diagnostics) { - state.semanticDiagnosticsPerFile!.set(sourceFilePath, diagnostics); + state.semanticDiagnosticsPerFile!.set(sourceFilePath, oldState!.hasReusableDiagnostic ? convertToDiagnostics(diagnostics as ReadonlyArray, newProgram) : diagnostics as ReadonlyArray); if (!state.semanticDiagnosticsFromOldState) { state.semanticDiagnosticsFromOldState = createMap(); } @@ -225,6 +252,40 @@ namespace ts { return state; } + function convertToDiagnostics(diagnostics: ReadonlyArray, newProgram: Program): ReadonlyArray { + if (!diagnostics.length) return emptyArray; + return diagnostics.map(diagnostic => { + const result: Diagnostic = convertToDiagnosticRelatedInformation(diagnostic, newProgram); + result.reportsUnnecessary = diagnostic.reportsUnnecessary; + result.source = diagnostic.source; + const { relatedInformation } = diagnostic; + result.relatedInformation = relatedInformation ? + relatedInformation.length ? + relatedInformation.map(r => convertToDiagnosticRelatedInformation(r, newProgram)) : + emptyArray : + undefined; + return result; + }); + } + + function convertToDiagnosticRelatedInformation(diagnostic: ReusableDiagnosticRelatedInformation, newProgram: Program): DiagnosticRelatedInformation { + const { file, messageText } = diagnostic; + return { + ...diagnostic, + file: file && newProgram.getSourceFileByPath(file), + messageText: messageText === undefined || isString(messageText) ? + messageText : + convertToDiagnosticMessageChain(messageText, newProgram) + }; + } + + function convertToDiagnosticMessageChain(diagnostic: ReusableDiagnosticMessageChain, newProgram: Program): DiagnosticMessageChain { + return { + ...diagnostic, + next: diagnostic.next && convertToDiagnosticMessageChain(diagnostic.next, newProgram) + }; + } + /** * Releases program and other related not needed properties */ @@ -514,12 +575,13 @@ namespace ts { return diagnostics; } + export type ProgramBuildInfoDiagnostic = string | [string, ReadonlyArray]; export interface ProgramBuildInfo { fileInfos: MapLike; options: CompilerOptions; referencedMap?: MapLike; exportedModulesMap?: MapLike; - semanticDiagnosticsPerFile?: string[]; + semanticDiagnosticsPerFile?: ProgramBuildInfoDiagnostic[]; } /** @@ -555,15 +617,58 @@ namespace ts { } if (state.semanticDiagnosticsPerFile) { - const semanticDiagnosticsPerFile: string[] = []; + const semanticDiagnosticsPerFile: ProgramBuildInfoDiagnostic[] = []; // Currently not recording actual errors since those mean no emit for tsc --build - state.semanticDiagnosticsPerFile.forEach((_value, key) => semanticDiagnosticsPerFile.push(key)); + state.semanticDiagnosticsPerFile.forEach((value, key) => semanticDiagnosticsPerFile.push( + value.length ? + [ + key, + state.hasReusableDiagnostic ? + value as ReadonlyArray : + convertToReusableDiagnostics(value as ReadonlyArray) + ] : + key + )); result.semanticDiagnosticsPerFile = semanticDiagnosticsPerFile; } return result; } + function convertToReusableDiagnostics(diagnostics: ReadonlyArray): ReadonlyArray { + Debug.assert(!!diagnostics.length); + return diagnostics.map(diagnostic => { + const result: ReusableDiagnostic = convertToReusableDiagnosticRelatedInformation(diagnostic); + result.reportsUnnecessary = diagnostic.reportsUnnecessary; + result.source = diagnostic.source; + const { relatedInformation } = diagnostic; + result.relatedInformation = relatedInformation ? + relatedInformation.length ? + relatedInformation.map(r => convertToReusableDiagnosticRelatedInformation(r)) : + emptyArray : + undefined; + return result; + }); + } + + function convertToReusableDiagnosticRelatedInformation(diagnostic: DiagnosticRelatedInformation): ReusableDiagnosticRelatedInformation { + const { file, messageText } = diagnostic; + return { + ...diagnostic, + file: file && file.path, + messageText: messageText === undefined || isString(messageText) ? + messageText : + convertToReusableDiagnosticMessageChain(messageText) + }; + } + + function convertToReusableDiagnosticMessageChain(diagnostic: DiagnosticMessageChain): ReusableDiagnosticMessageChain { + return { + ...diagnostic, + next: diagnostic.next && convertToReusableDiagnosticMessageChain(diagnostic.next) + }; + } + export enum BuilderProgramKind { SemanticDiagnosticsBuilderProgram, EmitAndSemanticDiagnosticsBuilderProgram @@ -868,7 +973,8 @@ namespace ts { compilerOptions: program.options, referencedMap: getMapOfReferencedSet(program.referencedMap), exportedModulesMap: getMapOfReferencedSet(program.exportedModulesMap), - semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, identity, () => emptyArray) + semanticDiagnosticsPerFile: program.semanticDiagnosticsPerFile && arrayToMap(program.semanticDiagnosticsPerFile, value => isString(value) ? value : value[0], value => isString(value) ? emptyArray : value[1]), + hasReusableDiagnostic: true }; return { getState: () => state,