From a06f0c3d9f97a99fb84cd7ee15433c2fe37655b8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 18 Oct 2017 17:15:02 -0700 Subject: [PATCH 001/100] Use builder state to emit instead --- src/compiler/builder.ts | 513 +++++++++--------- src/compiler/tsc.ts | 4 +- src/compiler/watch.ts | 40 +- src/harness/unittests/builder.ts | 9 +- src/server/project.ts | 27 +- .../reference/api/tsserverlibrary.d.ts | 67 ++- tests/baselines/reference/api/typescript.d.ts | 64 +++ 7 files changed, 408 insertions(+), 316 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 34ca1bdf0e9..9a60557ae36 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -11,10 +11,8 @@ namespace ts { writeByteOrderMark: boolean; text: string; } -} -/* @internal */ -namespace ts { + /* @internal */ export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput { const outputFiles: OutputFile[] = []; @@ -26,213 +24,270 @@ namespace ts { } } - export interface Builder { - /** Called to inform builder about new program */ - updateProgram(newProgram: Program): void; - - /** Gets the files affected by the file path */ - getFilesAffectedBy(program: Program, path: Path): ReadonlyArray; - - /** Emit the changed files and clear the cache of the changed files */ - emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray; - - /** When called gets the semantic diagnostics for the program. It also caches the diagnostics and manage them */ - getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray; - - /** Called to reset the status of the builder */ - clear(): void; + function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { + if (map1 === undefined) { + return map2 === undefined; + } + if (map2 === undefined) { + return map1 === undefined; + } + // Has same size and every key is present in both maps + return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); } - interface EmitHandler { + /** + * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) + * Note that it is only safe to pass BuilderState as old state when creating new state, when + * - If iterator's next method to get next affected file is never called + * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete + */ + export interface BuilderState { /** - * Called when sourceFile is added to the program + * The map of file infos, where there is entry for each file in the program + * The entry is signature of the file (from last emit) or empty string */ - onAddSourceFile(program: Program, sourceFile: SourceFile): void; + fileInfos: ReadonlyMap>; + /** - * Called when sourceFile is removed from the program + * Returns true if module gerneration is not ModuleKind.None */ - onRemoveSourceFile(path: Path): void; + isModuleEmit: boolean; + /** - * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. - * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; - * otherwise "onUpdateSourceFileWithSameVersion" will be called. + * Map of file referenced or undefined if it wasnt module emit + * The entry is present only if file references other files + * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) */ - onUpdateSourceFile(program: Program, sourceFile: SourceFile): void; + referencedMap: ReadonlyMap | undefined; + /** - * For all source files, either "onUpdateSourceFile" or "onUpdateSourceFileWithSameVersion" will be called. - * If the builder is sure that the source file needs an update, "onUpdateSourceFile" will be called; - * otherwise "onUpdateSourceFileWithSameVersion" will be called. - * This function should return whether the source file should be marked as changed (meaning that something associated with file has changed, e.g. module resolution) + * Set of source file's paths that have been changed, either in resolution or versions */ - onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean; + changedFilesSet: ReadonlyMap; + /** - * Gets the files affected by the script info which has updated shape from the known one + * Set of cached semantic diagnostics per file */ - getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray; + semanticDiagnosticsPerFile: ReadonlyMap>; + + /** + * Returns true if this state is safe to use as oldState + */ + canCreateNewStateFrom(): boolean; + + /** + * Gets the files affected by the file path + * This api is only for internal use + */ + /* @internal */ + getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray; + + /** + * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + */ + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + */ + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; } - interface FileInfo { + /** + * Information about the source file: Its version and optional signature from last emit + */ + export interface FileInfo { version: string; signature: string; } + export interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + export type ReferencedSet = ReadonlyMap; + export interface BuilderOptions { getCanonicalFileName: GetCanonicalFileName; computeHash: (data: string) => string; } - export function createBuilder(options: BuilderOptions): Builder { - let isModuleEmit: boolean | undefined; + export function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState { const fileInfos = createMap(); + const isModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; + const referencedMap = isModuleEmit ? createMap() : undefined; + const semanticDiagnosticsPerFile = createMap>(); /** The map has key by source file's path that has been changed */ const changedFilesSet = createMap(); const hasShapeChanged = createMap(); let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; - let emitHandler: EmitHandler; + + // Iterator datas + let affectedFiles: ReadonlyArray | undefined; + let affectedFilesIndex = 0; + const seenAffectedFiles = createMap(); + const getEmitDependentFilesAffectedBy = isModuleEmit ? + getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit; + + const useOldState = oldState && oldState.isModuleEmit === isModuleEmit; + if (useOldState) { + Debug.assert(oldState.canCreateNewStateFrom(), "Cannot use this state as old state"); + Debug.assert(!forEachEntry(oldState.changedFilesSet, (_value, path) => oldState.semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); + + copyEntries(oldState.changedFilesSet, changedFilesSet); + copyEntries(oldState.semanticDiagnosticsPerFile, semanticDiagnosticsPerFile); + } + + for (const sourceFile of newProgram.getSourceFiles()) { + const version = sourceFile.version; + let oldInfo: Readonly; + let oldReferences: ReferencedSet; + const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); + + // Register changed file + // if not using old state so every file is changed + if (!useOldState || + // File wasnt present earlier + !(oldInfo = oldState.fileInfos.get(sourceFile.path)) || + // versions dont match + oldInfo.version !== version || + // Referenced files changed + !hasSameKeys(newReferences, (oldReferences = oldState.referencedMap && oldState.referencedMap.get(sourceFile.path))) || + // Referenced file was deleted + newReferences && forEachEntry(newReferences, (_value, path) => oldState.fileInfos.has(path) && !newProgram.getSourceFileByPath(path as Path))) { + changedFilesSet.set(sourceFile.path, true); + // All changed files need to re-evaluate its semantic diagnostics + semanticDiagnosticsPerFile.delete(sourceFile.path); + } + + newReferences && referencedMap.set(sourceFile.path, newReferences); + fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); + } + + // For removed files, remove the semantic diagnostics removed files as changed + useOldState && oldState.fileInfos.forEach((_value, path) => !fileInfos.has(path) && semanticDiagnosticsPerFile.delete(path)); + + // Set the old state and program to undefined to ensure we arent keeping them alive hence forward + oldState = undefined; + newProgram = undefined; + return { - updateProgram, + fileInfos, + isModuleEmit, + referencedMap, + changedFilesSet, + semanticDiagnosticsPerFile, + canCreateNewStateFrom, getFilesAffectedBy, - emitChangedFiles, - getSemanticDiagnostics, - clear + emitNextAffectedFile, + getSemanticDiagnostics }; - function createProgramGraph(program: Program) { - const currentIsModuleEmit = program.getCompilerOptions().module !== ModuleKind.None; - if (isModuleEmit !== currentIsModuleEmit) { - isModuleEmit = currentIsModuleEmit; - emitHandler = isModuleEmit ? getModuleEmitHandler() : getNonModuleEmitHandler(); - fileInfos.clear(); - semanticDiagnosticsPerFile.clear(); - } - hasShapeChanged.clear(); - allFilesExcludingDefaultLibraryFile = undefined; - mutateMap( - fileInfos, - arrayToMap(program.getSourceFiles(), sourceFile => sourceFile.path), - { - // Add new file info - createNewValue: (_path, sourceFile) => addNewFileInfo(program, sourceFile), - // Remove existing file info - onDeleteValue: removeExistingFileInfo, - // We will update in place instead of deleting existing value and adding new one - onExistingValue: (existingInfo, sourceFile) => updateExistingFileInfo(program, existingInfo, sourceFile) - } - ); + /** + * Can use this state as old State if we have iterated through all affected files present + */ + function canCreateNewStateFrom() { + return !affectedFiles || affectedFiles.length <= affectedFilesIndex; } - function registerChangedFile(path: Path) { - changedFilesSet.set(path, true); - // All changed files need to re-evaluate its semantic diagnostics - semanticDiagnosticsPerFile.delete(path); - } - - function addNewFileInfo(program: Program, sourceFile: SourceFile): FileInfo { - registerChangedFile(sourceFile.path); - emitHandler.onAddSourceFile(program, sourceFile); - return { version: sourceFile.version, signature: undefined }; - } - - function removeExistingFileInfo(_existingFileInfo: FileInfo, path: Path) { - // Since we dont need to track removed file as changed file - // We can just remove its diagnostics - changedFilesSet.delete(path); - semanticDiagnosticsPerFile.delete(path); - emitHandler.onRemoveSourceFile(path); - } - - function updateExistingFileInfo(program: Program, existingInfo: FileInfo, sourceFile: SourceFile) { - if (existingInfo.version !== sourceFile.version) { - registerChangedFile(sourceFile.path); - existingInfo.version = sourceFile.version; - emitHandler.onUpdateSourceFile(program, sourceFile); - } - else if (emitHandler.onUpdateSourceFileWithSameVersion(program, sourceFile)) { - registerChangedFile(sourceFile.path); - } - } - - function ensureProgramGraph(program: Program) { - if (!emitHandler) { - createProgramGraph(program); - } - } - - function updateProgram(newProgram: Program) { - if (emitHandler) { - createProgramGraph(newProgram); - } - } - - function getFilesAffectedBy(program: Program, path: Path): ReadonlyArray { - ensureProgramGraph(program); - - const sourceFile = program.getSourceFileByPath(path); + /** + * Gets the files affected by the path from the program + */ + function getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray { + const sourceFile = programOfThisState.getSourceFileByPath(path); if (!sourceFile) { return emptyArray; } - if (!updateShapeSignature(program, sourceFile)) { + if (!updateShapeSignature(programOfThisState, sourceFile)) { return [sourceFile]; } - return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile); + + return getEmitDependentFilesAffectedBy(programOfThisState, sourceFile); } - function emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray { - ensureProgramGraph(program); - const compilerOptions = program.getCompilerOptions(); + /** + * Emits the next affected file, and returns the EmitResult along with source files emitted + * Returns undefined when iteration is complete + */ + function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined { + if (affectedFiles) { + while (affectedFilesIndex < affectedFiles.length) { + const affectedFile = affectedFiles[affectedFilesIndex]; + affectedFilesIndex++; + if (!seenAffectedFiles.has(affectedFile.path)) { + seenAffectedFiles.set(affectedFile.path, true); - if (!changedFilesSet.size) { - return emptyArray; + // Emit the affected file + const result = programOfThisState.emit(affectedFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers) as AffectedFileEmitResult; + result.affectedFile = affectedFile; + return result; + } + } + + affectedFiles = undefined; } + // Get next changed file + const nextKey = changedFilesSet.keys().next(); + if (nextKey.done) { + // Done + return undefined; + } + + const compilerOptions = programOfThisState.getCompilerOptions(); // With --out or --outFile all outputs go into single file, do it only once if (compilerOptions.outFile || compilerOptions.out) { Debug.assert(semanticDiagnosticsPerFile.size === 0); changedFilesSet.clear(); - return [program.emit(/*targetSourceFile*/ undefined, writeFileCallback)]; + return programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers); } - const seenFiles = createMap(); - let result: EmitResult[] | undefined; - changedFilesSet.forEach((_true, path) => { - // Get the affected Files by this program - const affectedFiles = getFilesAffectedBy(program, path as Path); - affectedFiles.forEach(affectedFile => { - // Affected files shouldnt have cached diagnostics - semanticDiagnosticsPerFile.delete(affectedFile.path); + // Get next batch of affected files + changedFilesSet.delete(nextKey.value); + affectedFilesIndex = 0; + affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path); - if (!seenFiles.has(affectedFile.path)) { - seenFiles.set(affectedFile.path, true); + // Clear the semantic diagnostic of affected files + affectedFiles.forEach(affectedFile => semanticDiagnosticsPerFile.delete(affectedFile.path)); - // Emit the affected file - (result || (result = [])).push(program.emit(affectedFile, writeFileCallback)); - } - }); - }); - changedFilesSet.clear(); - return result || emptyArray; + return emitNextAffectedFile(programOfThisState, writeFileCallback, cancellationToken, customTransformers); } - function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray { - ensureProgramGraph(program); - Debug.assert(changedFilesSet.size === 0); - - const compilerOptions = program.getCompilerOptions(); + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + */ + function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { + const compilerOptions = programOfThisState.getCompilerOptions(); if (compilerOptions.outFile || compilerOptions.out) { Debug.assert(semanticDiagnosticsPerFile.size === 0); // We dont need to cache the diagnostics just return them from program - return program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken); + return programOfThisState.getSemanticDiagnostics(sourceFile, cancellationToken); + } + + if (sourceFile) { + return getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken); } let diagnostics: Diagnostic[]; - for (const sourceFile of program.getSourceFiles()) { - diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(program, sourceFile, cancellationToken)); + for (const sourceFile of programOfThisState.getSourceFiles()) { + diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken)); } return diagnostics || emptyArray; } + /** + * Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set + */ function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { const path = sourceFile.path; const cachedDiagnostics = semanticDiagnosticsPerFile.get(path); @@ -247,15 +302,6 @@ namespace ts { return diagnostics; } - function clear() { - isModuleEmit = undefined; - emitHandler = undefined; - fileInfos.clear(); - semanticDiagnosticsPerFile.clear(); - changedFilesSet.clear(); - hasShapeChanged.clear(); - } - /** * For script files that contains only ambient external modules, although they are not actually external module files, * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, @@ -271,9 +317,10 @@ namespace ts { return true; } - /** - * @return {boolean} indicates if the shape signature has changed since last update. - */ + /** + * Returns if the shape of the signature has changed since last emit + * Note that it also updates the current signature as the latest signature for the file + */ function updateShapeSignature(program: Program, sourceFile: SourceFile) { Debug.assert(!!sourceFile); @@ -360,6 +407,15 @@ namespace ts { } } + /** + * Gets the files referenced by the the file path + */ + function getReferencedByPaths(referencedFilePath: Path) { + return mapDefinedIter(referencedMap.entries(), ([filePath, referencesInFile]) => + referencesInFile.has(referencedFilePath) ? filePath as Path : undefined + ); + } + /** * Gets all files of the program excluding the default library file */ @@ -386,126 +442,53 @@ namespace ts { } } - function getNonModuleEmitHandler(): EmitHandler { - return { - onAddSourceFile: noop, - onRemoveSourceFile: noop, - onUpdateSourceFile: noop, - onUpdateSourceFileWithSameVersion: returnFalse, - getFilesAffectedByUpdatedShape - }; - - function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray { - const options = program.getCompilerOptions(); - // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, - // so returning the file itself is good enough. - if (options && (options.out || options.outFile)) { - return [sourceFile]; - } - return getAllFilesExcludingDefaultLibraryFile(program, sourceFile); + /** + * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { + const compilerOptions = programOfThisState.getCompilerOptions(); + // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, + // so returning the file itself is good enough. + if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; } + return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); } - function getModuleEmitHandler(): EmitHandler { - const references = createMap>(); - return { - onAddSourceFile: setReferences, - onRemoveSourceFile, - onUpdateSourceFile: updateReferences, - onUpdateSourceFileWithSameVersion: updateReferencesTrackingChangedReferences, - getFilesAffectedByUpdatedShape - }; - - function setReferences(program: Program, sourceFile: SourceFile) { - const newReferences = getReferencedFiles(program, sourceFile); - if (newReferences) { - references.set(sourceFile.path, newReferences); - } + /** + * When program emits modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { + if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { + return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); } - function updateReferences(program: Program, sourceFile: SourceFile) { - const newReferences = getReferencedFiles(program, sourceFile); - if (newReferences) { - references.set(sourceFile.path, newReferences); - } - else { - references.delete(sourceFile.path); - } + const compilerOptions = programOfThisState.getCompilerOptions(); + if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; } - function updateReferencesTrackingChangedReferences(program: Program, sourceFile: SourceFile) { - const newReferences = getReferencedFiles(program, sourceFile); - if (!newReferences) { - // Changed if we had references - return references.delete(sourceFile.path); - } + // Now we need to if each file in the referencedBy list has a shape change as well. + // Because if so, its own referencedBy files need to be saved as well to make the + // emitting result consistent with files on disk. + const seenFileNamesMap = createMap(); - const oldReferences = references.get(sourceFile.path); - references.set(sourceFile.path, newReferences); - if (!oldReferences || oldReferences.size !== newReferences.size) { - return true; - } - - // If there are any new references that werent present previously there is change - return forEachEntry(newReferences, (_true, referencedPath) => !oldReferences.delete(referencedPath)) || - // Otherwise its changed if there are more references previously than now - !!oldReferences.size; - } - - function onRemoveSourceFile(removedFilePath: Path) { - // Remove existing references - references.forEach((referencesInFile, filePath) => { - if (referencesInFile.has(removedFilePath)) { - // add files referencing the removedFilePath, as changed files too - const referencedByInfo = fileInfos.get(filePath); - if (referencedByInfo) { - registerChangedFile(filePath as Path); - } - } - }); - // Delete the entry for the removed file path - references.delete(removedFilePath); - } - - function getReferencedByPaths(referencedFilePath: Path) { - return mapDefinedIter(references.entries(), ([filePath, referencesInFile]) => - referencesInFile.has(referencedFilePath) ? filePath as Path : undefined - ); - } - - function getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray { - if (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile)) { - return getAllFilesExcludingDefaultLibraryFile(program, sourceFile); - } - - const compilerOptions = program.getCompilerOptions(); - if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { - return [sourceFile]; - } - - // Now we need to if each file in the referencedBy list has a shape change as well. - // Because if so, its own referencedBy files need to be saved as well to make the - // emitting result consistent with files on disk. - const seenFileNamesMap = createMap(); - - // Start with the paths this file was referenced by - const path = sourceFile.path; - seenFileNamesMap.set(path, sourceFile); - const queue = getReferencedByPaths(path); - while (queue.length > 0) { - const currentPath = queue.pop(); - if (!seenFileNamesMap.has(currentPath)) { - const currentSourceFile = program.getSourceFileByPath(currentPath); - seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(program, currentSourceFile)) { - queue.push(...getReferencedByPaths(currentPath)); - } + // Start with the paths this file was referenced by + seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape); + const queue = getReferencedByPaths(sourceFileWithUpdatedShape.path); + while (queue.length > 0) { + const currentPath = queue.pop(); + if (!seenFileNamesMap.has(currentPath)) { + const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); + seenFileNamesMap.set(currentPath, currentSourceFile); + if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile)) { + queue.push(...getReferencedByPaths(currentPath)); } } - - // Return array of values that needs emit - return flatMapIter(seenFileNamesMap.values(), value => value); } + + // Return array of values that needs emit + return flatMapIter(seenFileNamesMap.values(), value => value); } } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 01fb45e4f7d..ab1e5cc566e 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -155,8 +155,8 @@ namespace ts { const watchingHost = ts.createWatchingSystemHost(/*pretty*/ undefined, sys, parseConfigFile, reportDiagnostic, reportWatchDiagnostic); watchingHost.beforeCompile = enableStatistics; const afterCompile = watchingHost.afterCompile; - watchingHost.afterCompile = (host, program, builder) => { - afterCompile(host, program, builder); + watchingHost.afterCompile = (host, program) => { + afterCompile(host, program); reportStatistics(program); }; return watchingHost; diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 8e49bb71a15..96605f7b6b9 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -2,7 +2,6 @@ /// /// -/* @internal */ namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; export type ParseConfigFile = (configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter) => ParsedCommandLine; @@ -19,7 +18,7 @@ namespace ts { // Callbacks to do custom action before creating program and after creating program beforeCompile(compilerOptions: CompilerOptions): void; - afterCompile(host: DirectoryStructureHost, program: Program, builder: Builder): void; + afterCompile(host: DirectoryStructureHost, program: Program): void; } const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { @@ -133,6 +132,11 @@ namespace ts { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system, pretty ? reportDiagnosticWithColorAndContext : reportDiagnosticSimply); reportWatchDiagnostic = reportWatchDiagnostic || createWatchDiagnosticReporter(system); parseConfigFile = parseConfigFile || ts.parseConfigFile; + let builderState: Readonly | undefined; + const options: BuilderOptions = { + getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), + computeHash: data => system.createHash ? system.createHash(data) : data + }; return { system, parseConfigFile, @@ -142,7 +146,9 @@ namespace ts { afterCompile: compileWatchedProgram, }; - function compileWatchedProgram(host: DirectoryStructureHost, program: Program, builder: Builder) { + function compileWatchedProgram(host: DirectoryStructureHost, program: Program) { + builderState = createBuilderState(program, options, builderState); + // First get and report any syntactic errors. const diagnostics = program.getSyntacticDiagnostics().slice(); let reportSemanticDiagnostics = false; @@ -163,22 +169,15 @@ namespace ts { let sourceMaps: SourceMapData[]; let emitSkipped: boolean; - const result = builder.emitChangedFiles(program, writeFile); - if (result.length === 0) { - emitSkipped = true; - } - else { - for (const emitOutput of result) { - if (emitOutput.emitSkipped) { - emitSkipped = true; - } - addRange(diagnostics, emitOutput.diagnostics); - sourceMaps = concatenate(sourceMaps, emitOutput.sourceMaps); - } + let affectedEmitResult: AffectedFileEmitResult; + while (affectedEmitResult = builderState.emitNextAffectedFile(program, writeFile)) { + emitSkipped = emitSkipped || affectedEmitResult.emitSkipped; + addRange(diagnostics, affectedEmitResult.diagnostics); + sourceMaps = addRange(sourceMaps, affectedEmitResult.sourceMaps); } if (reportSemanticDiagnostics) { - addRange(diagnostics, builder.getSemanticDiagnostics(program)); + addRange(diagnostics, builderState.getSemanticDiagnostics(program)); } return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped, diagnostics, reportDiagnostic); @@ -299,8 +298,6 @@ namespace ts { getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) : getCurrentDirectory() ); - // There is no extra check needed since we can just rely on the program to decide emit - const builder = createBuilder({ getCanonicalFileName, computeHash }); synchronizeProgram(); @@ -334,7 +331,6 @@ namespace ts { compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames; program = createProgram(rootFileNames, compilerOptions, compilerHost, program); resolutionCache.finishCachingPerDirectoryResolution(); - builder.updateProgram(program); // Update watches updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath); @@ -356,7 +352,7 @@ namespace ts { missingFilePathsRequestedForRelease = undefined; } - afterCompile(directoryStructureHost, program, builder); + afterCompile(directoryStructureHost, program); reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); } @@ -640,9 +636,5 @@ namespace ts { flags ); } - - function computeHash(data: string) { - return system.createHash ? system.createHash(data) : data; - } } } diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index bfdeb1a4067..60297d2b7fc 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -44,15 +44,16 @@ namespace ts { }); function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { - const builder = createBuilder({ + let builderState: BuilderState; + const builderOptions: BuilderOptions = { getCanonicalFileName: identity, computeHash: identity - }); + }; return fileNames => { const program = getProgram(); - builder.updateProgram(program); + builderState = createBuilderState(program, builderOptions, builderState); const outputFileNames: string[] = []; - builder.emitChangedFiles(program, fileName => outputFileNames.push(fileName)); + while (builderState.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { } assert.deepEqual(outputFileNames, fileNames); }; } diff --git a/src/server/project.ts b/src/server/project.ts index ce750c78e23..056fedf19af 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -139,7 +139,7 @@ namespace ts.server { /*@internal*/ resolutionCache: ResolutionCache; - private builder: Builder; + private builderState: BuilderState; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -442,15 +442,6 @@ namespace ts.server { return this.languageService; } - private ensureBuilder() { - if (!this.builder) { - this.builder = createBuilder({ - getCanonicalFileName: this.projectService.toCanonicalFileName, - computeHash: data => this.projectService.host.createHash(data) - }); - } - } - private shouldEmitFile(scriptInfo: ScriptInfo) { return scriptInfo && !scriptInfo.isDynamicOrHasMixedContent(); } @@ -460,8 +451,11 @@ namespace ts.server { return []; } this.updateGraph(); - this.ensureBuilder(); - return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path), + this.builderState = createBuilderState(this.program, { + getCanonicalFileName: this.projectService.toCanonicalFileName, + computeHash: data => this.projectService.host.createHash(data) + }, this.builderState); + return mapDefined(this.builderState.getFilesAffectedBy(this.program, scriptInfo.path), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined); } @@ -497,6 +491,7 @@ namespace ts.server { } this.languageService.cleanupSemanticCache(); this.languageServiceEnabled = false; + this.builderState = undefined; this.resolutionCache.closeTypeRootsWatch(); this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false); } @@ -537,7 +532,7 @@ namespace ts.server { this.rootFilesMap = undefined; this.externalFiles = undefined; this.program = undefined; - this.builder = undefined; + this.builderState = undefined; this.resolutionCache.clear(); this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; @@ -787,15 +782,9 @@ namespace ts.server { if (this.setTypings(cachedTypings)) { hasChanges = this.updateGraphWorker() || hasChanges; } - if (this.builder) { - this.builder.updateProgram(this.program); - } } else { this.lastCachedUnresolvedImportsList = undefined; - if (this.builder) { - this.builder.clear(); - } } if (hasChanges) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 231d7bae3f2..3ec16254780 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3771,6 +3771,70 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } + /** + * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) + * Note that it is only safe to pass BuilderState as old state when creating new state, when + * - If iterator's next method to get next affected file is never called + * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete + */ + interface BuilderState { + /** + * The map of file infos, where there is entry for each file in the program + * The entry is signature of the file (from last emit) or empty string + */ + fileInfos: ReadonlyMap>; + /** + * Returns true if module gerneration is not ModuleKind.None + */ + isModuleEmit: boolean; + /** + * Map of file referenced or undefined if it wasnt module emit + * The entry is present only if file references other files + * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) + */ + referencedMap: ReadonlyMap | undefined; + /** + * Set of source file's paths that have been changed, either in resolution or versions + */ + changedFilesSet: ReadonlyMap; + /** + * Set of cached semantic diagnostics per file + */ + semanticDiagnosticsPerFile: ReadonlyMap>; + /** + * Returns true if this state is safe to use as oldState + */ + canCreateNewStateFrom(): boolean; + /** + * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + */ + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + */ + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + /** + * Information about the source file: Its version and optional signature from last emit + */ + interface FileInfo { + version: string; + signature: string; + } + interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + type ReferencedSet = ReadonlyMap; + interface BuilderOptions { + getCanonicalFileName: (fileName: string) => string; + computeHash: (data: string) => string; + } + function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; @@ -7210,7 +7274,7 @@ declare namespace ts.server { languageServiceEnabled: boolean; readonly trace?: (s: string) => void; readonly realpath?: (path: string) => string; - private builder; + private builderState; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -7271,7 +7335,6 @@ declare namespace ts.server { getGlobalProjectErrors(): ReadonlyArray; getAllProjectErrors(): ReadonlyArray; getLanguageService(ensureSynchronized?: boolean): LanguageService; - private ensureBuilder(); private shouldEmitFile(scriptInfo); getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[]; /** diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6b2db71b140..5dec286a8b7 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3718,6 +3718,70 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } + /** + * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) + * Note that it is only safe to pass BuilderState as old state when creating new state, when + * - If iterator's next method to get next affected file is never called + * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete + */ + interface BuilderState { + /** + * The map of file infos, where there is entry for each file in the program + * The entry is signature of the file (from last emit) or empty string + */ + fileInfos: ReadonlyMap>; + /** + * Returns true if module gerneration is not ModuleKind.None + */ + isModuleEmit: boolean; + /** + * Map of file referenced or undefined if it wasnt module emit + * The entry is present only if file references other files + * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) + */ + referencedMap: ReadonlyMap | undefined; + /** + * Set of source file's paths that have been changed, either in resolution or versions + */ + changedFilesSet: ReadonlyMap; + /** + * Set of cached semantic diagnostics per file + */ + semanticDiagnosticsPerFile: ReadonlyMap>; + /** + * Returns true if this state is safe to use as oldState + */ + canCreateNewStateFrom(): boolean; + /** + * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + */ + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + */ + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + /** + * Information about the source file: Its version and optional signature from last emit + */ + interface FileInfo { + version: string; + signature: string; + } + interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + type ReferencedSet = ReadonlyMap; + interface BuilderOptions { + getCanonicalFileName: (fileName: string) => string; + computeHash: (data: string) => string; + } + function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; From 576fe1e995973b96c98d7152af5d27e34dec2d0e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 26 Oct 2017 10:00:23 -0700 Subject: [PATCH 002/100] Expose the watch and builder API in the typescript.d.ts --- src/compiler/tsc.ts | 62 +++- src/compiler/watch.ts | 326 +++++++++++------- .../unittests/reuseProgramStructure.ts | 32 +- src/harness/unittests/tscWatchMode.ts | 123 +++---- src/harness/virtualFileSystemWithWatch.ts | 2 +- src/services/tsconfig.json | 6 +- tests/baselines/reference/api/typescript.d.ts | 66 ++++ 7 files changed, 382 insertions(+), 235 deletions(-) diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index ab1e5cc566e..5342319e75d 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -21,10 +21,10 @@ namespace ts { return diagnostic.messageText; } - let reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticSimply); + let reportDiagnostic = createDiagnosticReporter(); function udpateReportDiagnostic(options: CompilerOptions) { if (options.pretty) { - reportDiagnostic = createDiagnosticReporter(sys, reportDiagnosticWithColorAndContext); + reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true); } } @@ -55,7 +55,7 @@ namespace ts { // If there are any errors due to command line parsing and/or // setting up localization, report them and quit. if (commandLine.errors.length > 0) { - reportDiagnostics(commandLine.errors, reportDiagnostic); + commandLine.errors.forEach(reportDiagnostic); return sys.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } @@ -110,12 +110,11 @@ namespace ts { const commandLineOptions = commandLine.options; if (configFileName) { - const reportWatchDiagnostic = createWatchDiagnosticReporter(); - const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic, reportWatchDiagnostic); + const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic); udpateReportDiagnostic(configParseResult.options); if (isWatchSet(configParseResult.options)) { reportWatchModeWithoutSysSupport(); - createWatchModeWithConfigFile(configParseResult, commandLineOptions, createWatchingSystemHost(reportWatchDiagnostic)); + createWatchOfConfigFile(configParseResult, commandLineOptions); } else { performCompilation(configParseResult.fileNames, configParseResult.options); @@ -125,7 +124,7 @@ namespace ts { udpateReportDiagnostic(commandLineOptions); if (isWatchSet(commandLineOptions)) { reportWatchModeWithoutSysSupport(); - createWatchModeWithoutConfigFile(commandLine.fileNames, commandLineOptions, createWatchingSystemHost()); + createWatchOfFilesAndCompilerOptions(commandLine.fileNames, commandLineOptions); } else { performCompilation(commandLine.fileNames, commandLineOptions); @@ -151,15 +150,37 @@ namespace ts { return sys.exit(exitStatus); } - function createWatchingSystemHost(reportWatchDiagnostic?: DiagnosticReporter) { - const watchingHost = ts.createWatchingSystemHost(/*pretty*/ undefined, sys, parseConfigFile, reportDiagnostic, reportWatchDiagnostic); - watchingHost.beforeCompile = enableStatistics; - const afterCompile = watchingHost.afterCompile; - watchingHost.afterCompile = (host, program) => { - afterCompile(host, program); + function createProgramCompilerWithBuilderState() { + const compilerWithBuilderState = ts.createProgramCompilerWithBuilderState(sys, reportDiagnostic); + return (host: DirectoryStructureHost, program: Program) => { + compilerWithBuilderState(host, program); reportStatistics(program); }; - return watchingHost; + } + + function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { + createWatch({ + system: sys, + beforeProgramCreate: enableStatistics, + afterProgramCreate: createProgramCompilerWithBuilderState(), + onConfigFileDiagnostic: reportDiagnostic, + rootFiles: configParseResult.fileNames, + options: configParseResult.options, + configFileName: configParseResult.options.configFilePath, + optionsToExtend, + configFileSpecs: configParseResult.configFileSpecs, + configFileWildCardDirectories: configParseResult.wildcardDirectories + }); + } + + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { + createWatch({ + system: sys, + beforeProgramCreate: enableStatistics, + afterProgramCreate: createProgramCompilerWithBuilderState(), + rootFiles, + options + }); } function compileProgram(program: Program): ExitStatus { @@ -182,7 +203,18 @@ namespace ts { const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); addRange(diagnostics, emitDiagnostics); - return handleEmitOutputAndReportErrors(sys, program, emittedFiles, emitSkipped, diagnostics, reportDiagnostic); + sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); + writeFileAndEmittedFileList(sys, program, emittedFiles); + if (emitSkipped && diagnostics.length > 0) { + // If the emitter didn't emit anything, then pass that value along. + return ExitStatus.DiagnosticsPresent_OutputsSkipped; + } + else if (diagnostics.length > 0) { + // The emitter emitted something, inform the caller if that happened in the presence + // of diagnostics or not. + return ExitStatus.DiagnosticsPresent_OutputsGenerated; + } + return ExitStatus.Success; } function enableStatistics(compilerOptions: CompilerOptions) { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 96605f7b6b9..0ff0d2f6122 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -4,149 +4,97 @@ namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; - export type ParseConfigFile = (configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter) => ParsedCommandLine; - export interface WatchingSystemHost { - // FS system to use - system: System; - // parse config file - parseConfigFile: ParseConfigFile; - - // Reporting errors - reportDiagnostic: DiagnosticReporter; - reportWatchDiagnostic: DiagnosticReporter; - - // Callbacks to do custom action before creating program and after creating program - beforeCompile(compilerOptions: CompilerOptions): void; - afterCompile(host: DirectoryStructureHost, program: Program): void; - } - - const defaultFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { + const sysFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { getCurrentDirectory: () => sys.getCurrentDirectory(), getNewLine: () => sys.newLine, getCanonicalFileName: createGetCanonicalFileName(sys.useCaseSensitiveFileNames) } : undefined; - export function createDiagnosticReporter(system = sys, worker = reportDiagnosticSimply, formatDiagnosticsHost?: FormatDiagnosticsHost): DiagnosticReporter { - return diagnostic => worker(diagnostic, getFormatDiagnosticsHost(), system); - - function getFormatDiagnosticsHost() { - return formatDiagnosticsHost || (formatDiagnosticsHost = system === sys ? defaultFormatDiagnosticsHost : { - getCurrentDirectory: () => system.getCurrentDirectory(), - getNewLine: () => system.newLine, - getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), - }); + /** + * Create a function that reports error by writing to the system and handles the formating of the diagnostic + */ + /*@internal*/ + export function createDiagnosticReporter(system = sys, pretty?: boolean): DiagnosticReporter { + const host: FormatDiagnosticsHost = system === sys ? sysFormatDiagnosticsHost : { + getCurrentDirectory: () => system.getCurrentDirectory(), + getNewLine: () => system.newLine, + getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), + }; + if (!pretty) { + return diagnostic => system.write(ts.formatDiagnostic(diagnostic, host)); } - } - export function createWatchDiagnosticReporter(system = sys): DiagnosticReporter { + const diagnostics: Diagnostic[] = new Array(1); return diagnostic => { - let output = new Date().toLocaleTimeString() + " - "; - output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine + system.newLine}`; - system.write(output); + diagnostics[0] = diagnostic; + system.write(formatDiagnosticsWithColorAndContext(diagnostics, host) + host.getNewLine()); + diagnostics[0] = undefined; }; } - export function reportDiagnostics(diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter): void { - for (const diagnostic of diagnostics) { - reportDiagnostic(diagnostic); - } - } - - export function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void { - system.write(ts.formatDiagnostic(diagnostic, host)); - } - - export function reportDiagnosticWithColorAndContext(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void { - system.write(ts.formatDiagnosticsWithColorAndContext([diagnostic], host) + host.getNewLine()); - } - - export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter, reportWatchDiagnostic: DiagnosticReporter): ParsedCommandLine { + /** + * Reads the config file, reports errors if any and exits if the config file cannot be found + */ + /*@internal*/ + export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter): ParsedCommandLine { let configFileText: string; try { configFileText = system.readFile(configFileName); } catch (e) { const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); - reportWatchDiagnostic(error); + reportDiagnostic(error); system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); return; } if (!configFileText) { const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - reportDiagnostics([error], reportDiagnostic); + reportDiagnostic(error); system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); return; } const result = parseJsonText(configFileName, configFileText); - reportDiagnostics(result.parseDiagnostics, reportDiagnostic); + result.parseDiagnostics.forEach(reportDiagnostic); const cwd = system.getCurrentDirectory(); const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); - reportDiagnostics(configParseResult.errors, reportDiagnostic); + configParseResult.errors.forEach(reportDiagnostic); return configParseResult; } - function reportEmittedFiles(files: string[], system: DirectoryStructureHost): void { - if (!files || files.length === 0) { - return; - } + /** + * Writes emitted files, source files depending on options + */ + /*@internal*/ + export function writeFileAndEmittedFileList(system: System, program: Program, emittedFiles: string[]) { const currentDir = system.getCurrentDirectory(); - for (const file of files) { + forEach(emittedFiles, file => { const filepath = getNormalizedAbsolutePath(file, currentDir); system.write(`TSFILE: ${filepath}${system.newLine}`); - } - } - - export function handleEmitOutputAndReportErrors(system: DirectoryStructureHost, program: Program, - emittedFiles: string[], emitSkipped: boolean, - diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter - ): ExitStatus { - reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), reportDiagnostic); - reportEmittedFiles(emittedFiles, system); + }); if (program.getCompilerOptions().listFiles) { forEach(program.getSourceFiles(), file => { system.write(file.fileName + system.newLine); }); } - - if (emitSkipped && diagnostics.length > 0) { - // If the emitter didn't emit anything, then pass that value along. - return ExitStatus.DiagnosticsPresent_OutputsSkipped; - } - else if (diagnostics.length > 0) { - // The emitter emitted something, inform the caller if that happened in the presence - // of diagnostics or not. - return ExitStatus.DiagnosticsPresent_OutputsGenerated; - } - return ExitStatus.Success; } - export function createWatchingSystemHost(pretty?: DiagnosticStyle, system = sys, - parseConfigFile?: ParseConfigFile, reportDiagnostic?: DiagnosticReporter, - reportWatchDiagnostic?: DiagnosticReporter - ): WatchingSystemHost { - reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system, pretty ? reportDiagnosticWithColorAndContext : reportDiagnosticSimply); - reportWatchDiagnostic = reportWatchDiagnostic || createWatchDiagnosticReporter(system); - parseConfigFile = parseConfigFile || ts.parseConfigFile; + /** + * Creates the function that compiles the program by maintaining the builder state and also return diagnostic reporter + */ + export function createProgramCompilerWithBuilderState(system = sys, reportDiagnostic?: DiagnosticReporter) { + reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); let builderState: Readonly | undefined; const options: BuilderOptions = { getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), computeHash: data => system.createHash ? system.createHash(data) : data }; - return { - system, - parseConfigFile, - reportDiagnostic, - reportWatchDiagnostic, - beforeCompile: noop, - afterCompile: compileWatchedProgram, - }; - function compileWatchedProgram(host: DirectoryStructureHost, program: Program) { + return (host: DirectoryStructureHost, program: Program) => { builderState = createBuilderState(program, options, builderState); // First get and report any syntactic errors. @@ -179,8 +127,9 @@ namespace ts { if (reportSemanticDiagnostics) { addRange(diagnostics, builderState.getSemanticDiagnostics(program)); } - return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped, - diagnostics, reportDiagnostic); + + sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); + writeFileAndEmittedFileList(system, program, emittedFiles); function ensureDirectoriesExist(directoryPath: string) { if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { @@ -210,24 +159,120 @@ namespace ts { } } } + }; + } + + export interface WatchHost { + /** FS system to use */ + system: System; + + /** Custom action before creating the program */ + beforeProgramCreate(compilerOptions: CompilerOptions): void; + /** Custom action after new program creation is successful */ + afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + } + + /** + * Host to create watch with root files and options + */ + export interface WatchOfFilesAndCompilerOptionsHost extends WatchHost { + /** root files to use to generate program */ + rootFiles: string[]; + + /** Compiler options */ + options: CompilerOptions; + } + + /** + * Host to create watch with config file + */ + export interface WatchOfConfigFileHost extends WatchHost { + /** Name of the config file to compile */ + configFileName: string; + + /** Options to extend */ + optionsToExtend?: CompilerOptions; + + // Reports errors in the config file + onConfigFileDiagnostic(diagnostic: Diagnostic): void; + } + + /*@internal*/ + /** + * Host to create watch with config file that is already parsed (from tsc) + */ + export interface WatchOfConfigFileHost extends WatchHost { + rootFiles?: string[]; + options?: CompilerOptions; + optionsToExtend?: CompilerOptions; + configFileSpecs?: ConfigFileSpecs; + configFileWildCardDirectories?: MapLike; + } + + export interface Watch { + /** Synchronize the program with the changes */ + synchronizeProgram(): void; + /** Get current program */ + /*@internal*/ + getProgram(): Program; + } + + /** + * Creates the watch what generates program using the config file + */ + export interface WatchOfConfigFile extends Watch { + } + + /** + * Creates the watch that generates program using the root files and compiler options + */ + export interface WatchOfFilesAndCompilerOptions extends Watch { + /** Updates the root files in the program, only if this is not config file compilation */ + updateRootFileNames(fileNames: string[]): void; + } + + /** + * Create the watched program for config file + */ + export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { + return createWatch({ + system, + beforeProgramCreate: noop, + afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), + onConfigFileDiagnostic: reportDiagnostic || createDiagnosticReporter(system), + configFileName, + optionsToExtend + }); + } + + /** + * Create the watched program for root files and compiler options + */ + export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { + return createWatch({ + system, + beforeProgramCreate: noop, + afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), + rootFiles, + options + }); + } + + /** + * Creates the watch from the host for root files and compiler options + */ + export function createWatch(host: WatchOfFilesAndCompilerOptionsHost): WatchOfFilesAndCompilerOptions; + /** + * Creates the watch from the host for config file + */ + export function createWatch(host: WatchOfConfigFileHost): WatchOfConfigFile; + export function createWatch(host: WatchOfFilesAndCompilerOptionsHost | WatchOfConfigFileHost): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + interface HostFileInfo { + version: number; + sourceFile: SourceFile; + fileWatcher: FileWatcher; } - } - export function createWatchModeWithConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions = {}, watchingHost?: WatchingSystemHost) { - return createWatchMode(configParseResult.fileNames, configParseResult.options, watchingHost, configParseResult.options.configFilePath, configParseResult.configFileSpecs, configParseResult.wildcardDirectories, optionsToExtend); - } - - export function createWatchModeWithoutConfigFile(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost) { - return createWatchMode(rootFileNames, compilerOptions, watchingHost); - } - - interface HostFileInfo { - version: number; - sourceFile: SourceFile; - fileWatcher: FileWatcher; - } - - function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) { let program: Program; let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc let missingFilesMap: Map; // Map of file watchers for the missing files @@ -239,16 +284,21 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed + const { system, configFileName, onConfigFileDiagnostic, afterProgramCreate, beforeProgramCreate, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchOfConfigFileHost; + let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchOfConfigFileHost; + + // From tsc we want to get already parsed result and hence check for rootFileNames + const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; + if (configFileName && !rootFileNames) { + parseConfigFile(); + } + const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(system.newLine); } : noop; const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher; const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher; const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher; - watchingHost = watchingHost || createWatchingSystemHost(compilerOptions.pretty); - const { system, parseConfigFile, reportDiagnostic, reportWatchDiagnostic, beforeCompile, afterCompile } = watchingHost; - - const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; if (configFileName) { watchFile(system, configFileName, scheduleProgramReload, writeLog); } @@ -304,7 +354,9 @@ namespace ts { // Update the wild card directory watch watchConfigFileWildCardDirectories(); - return () => program; + return configFileName ? + { getProgram: () => program, synchronizeProgram } : + { getProgram: () => program, synchronizeProgram, updateRootFileNames }; function synchronizeProgram() { writeLog(`Synchronizing program`); @@ -321,7 +373,7 @@ namespace ts { return; } - beforeCompile(compilerOptions); + beforeProgramCreate(compilerOptions); // Compile the program const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program; @@ -352,10 +404,16 @@ namespace ts { missingFilePathsRequestedForRelease = undefined; } - afterCompile(directoryStructureHost, program); + afterProgramCreate(directoryStructureHost, program); reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); } + function updateRootFileNames(files: string[]) { + Debug.assert(!configFileName, "Cannot update root file names with config file watch mode"); + rootFileNames = files; + scheduleProgramUpdate(); + } + function toPath(fileName: string) { return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); } @@ -468,6 +526,10 @@ namespace ts { } } + function reportWatchDiagnostic(diagnostic: Diagnostic) { + system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); + } + // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. @@ -505,7 +567,7 @@ namespace ts { function reloadFileNamesFromConfigFile() { const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost); if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) { - reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); + onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); } rootFileNames = result.fileNames; @@ -519,19 +581,22 @@ namespace ts { const cachedHost = directoryStructureHost as CachedDirectoryStructureHost; cachedHost.clearCache(); - const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic); - rootFileNames = configParseResult.fileNames; - compilerOptions = configParseResult.options; + parseConfigFile(); hasChangedCompilerOptions = true; - configFileSpecs = configParseResult.configFileSpecs; - configFileWildCardDirectories = configParseResult.wildcardDirectories; - synchronizeProgram(); // Update the wild card directory watch watchConfigFileWildCardDirectories(); } + function parseConfigFile() { + const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, directoryStructureHost as CachedDirectoryStructureHost, onConfigFileDiagnostic); + rootFileNames = configParseResult.fileNames; + compilerOptions = configParseResult.options; + configFileSpecs = configParseResult.configFileSpecs; + configFileWildCardDirectories = configParseResult.wildcardDirectories; + } + function onSourceFileChange(fileName: string, eventKind: FileWatcherEventKind, path: Path) { updateCachedSystemWithFile(fileName, path, eventKind); const hostSourceFile = sourceFilesCache.get(path); @@ -590,11 +655,16 @@ namespace ts { } function watchConfigFileWildCardDirectories() { - updateWatchingWildcardDirectories( - watchedWildcardDirectories || (watchedWildcardDirectories = createMap()), - createMapFromTemplate(configFileWildCardDirectories), - watchWildcardDirectory - ); + if (configFileWildCardDirectories) { + updateWatchingWildcardDirectories( + watchedWildcardDirectories || (watchedWildcardDirectories = createMap()), + createMapFromTemplate(configFileWildCardDirectories), + watchWildcardDirectory + ); + } + else if (watchedWildcardDirectories) { + clearMap(watchedWildcardDirectories, closeFileWatcherOf); + } } function watchWildcardDirectory(directory: string, flags: WatchDirectoryFlags) { diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index fdccc8a7795..cc330300d33 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -871,7 +871,6 @@ namespace ts { }); }); - import TestSystem = ts.TestFSWithWatch.TestServerHost; type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; import createTestSystem = ts.TestFSWithWatch.createWatchedSystem; import libFile = ts.TestFSWithWatch.libFile; @@ -897,30 +896,21 @@ namespace ts { return JSON.parse(JSON.stringify(filesOrOptions)); } - function createWatchingSystemHost(host: TestSystem) { - return ts.createWatchingSystemHost(/*pretty*/ undefined, host); - } - - function verifyProgramWithoutConfigFile(watchingSystemHost: WatchingSystemHost, rootFiles: string[], options: CompilerOptions) { - const program = createWatchModeWithoutConfigFile(rootFiles, options, watchingSystemHost)(); + function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) { + const program = createWatchOfFilesAndCompilerOptions(rootFiles, options, system).getProgram(); verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); } - function getConfigParseResult(watchingSystemHost: WatchingSystemHost, configFileName: string) { - return parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic); - } - - function verifyProgramWithConfigFile(watchingSystemHost: WatchingSystemHost, configFile: string) { - const result = getConfigParseResult(watchingSystemHost, configFile); - const program = createWatchModeWithConfigFile(result, {}, watchingSystemHost)(); - const { fileNames, options } = getConfigParseResult(watchingSystemHost, configFile); + function verifyProgramWithConfigFile(system: System, configFileName: string) { + const program = createWatchOfConfigFile(configFileName, {}, system).getProgram(); + const { fileNames, options } = parseConfigFile(configFileName, {}, system, notImplemented); verifyProgramIsUptoDate(program, fileNames, options); } function verifyProgram(files: FileOrFolder[], rootFiles: string[], options: CompilerOptions, configFile: string) { - const watchingSystemHost = createWatchingSystemHost(createTestSystem(files)); - verifyProgramWithoutConfigFile(watchingSystemHost, rootFiles, options); - verifyProgramWithConfigFile(watchingSystemHost, configFile); + const system = createTestSystem(files); + verifyProgramWithoutConfigFile(system, rootFiles, options); + verifyProgramWithConfigFile(system, configFile); } it("has empty options", () => { @@ -1031,11 +1021,9 @@ namespace ts { }; const configFile: FileOrFolder = { path: "/src/tsconfig.json", - content: JSON.stringify({ compilerOptions, include: ["packages/**/ *.ts"] }) + content: JSON.stringify({ compilerOptions, include: ["packages/**/*.ts"] }) }; - - const watchingSystemHost = createWatchingSystemHost(createTestSystem([app, module1, module2, module3, libFile, configFile])); - verifyProgramWithConfigFile(watchingSystemHost, configFile.path); + verifyProgramWithConfigFile(createTestSystem([app, module1, module2, module3, libFile, configFile]), configFile.path); }); }); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 4e2d63cec90..a3647aead4a 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -22,23 +22,14 @@ namespace ts.tscWatch { checkFileNames(`Program rootFileNames`, program.getRootFileNames(), expectedFiles); } - function createWatchingSystemHost(system: WatchedSystem) { - return ts.createWatchingSystemHost(/*pretty*/ undefined, system); + function createWatchOfConfigFile(configFileName: string, host: WatchedSystem) { + const watch = ts.createWatchOfConfigFile(configFileName, {}, host); + return () => watch.getProgram(); } - function parseConfigFile(configFileName: string, watchingSystemHost: WatchingSystemHost) { - return ts.parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic); - } - - function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem) { - const watchingSystemHost = createWatchingSystemHost(host); - const configFileResult = parseConfigFile(configFilePath, watchingSystemHost); - return ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost); - } - - function createWatchModeWithoutConfigFile(fileNames: string[], host: WatchedSystem, options: CompilerOptions = {}) { - const watchingSystemHost = createWatchingSystemHost(host); - return ts.createWatchModeWithoutConfigFile(fileNames, options, watchingSystemHost); + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}) { + const watch = ts.createWatchOfFilesAndCompilerOptions(rootFiles, options, host); + return () => watch.getProgram(); } function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { @@ -190,7 +181,7 @@ namespace ts.tscWatch { content: `export let x: number` }; const host = createWatchedSystem([appFile, moduleFile, libFile]); - const watch = createWatchModeWithoutConfigFile([appFile.path], host); + const watch = createWatchOfFilesAndCompilerOptions([appFile.path], host); checkProgramActualFiles(watch(), [appFile.path, libFile.path, moduleFile.path]); @@ -215,7 +206,7 @@ namespace ts.tscWatch { const host = createWatchedSystem([f1, config], { useCaseSensitiveFileNames: false }); const upperCaseConfigFilePath = combinePaths(getDirectoryPath(config.path).toUpperCase(), getBaseFileName(config.path)); - const watch = createWatchModeWithConfigFile(upperCaseConfigFilePath, host); + const watch = createWatchOfConfigFile(upperCaseConfigFilePath, host); checkProgramActualFiles(watch(), [combinePaths(getDirectoryPath(upperCaseConfigFilePath), getBaseFileName(f1.path))]); }); @@ -244,14 +235,10 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([configFile, libFile, file1, file2, file3]); - const watchingSystemHost = createWatchingSystemHost(host); - const configFileResult = parseConfigFile(configFile.path, watchingSystemHost); - assert.equal(configFileResult.errors.length, 0, `expect no errors in config file, got ${JSON.stringify(configFileResult.errors)}`); + const watch = ts.createWatchOfConfigFile(configFile.path, {}, host, notImplemented); - const watch = ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost); - - checkProgramActualFiles(watch(), [file1.path, libFile.path, file2.path]); - checkProgramRootFiles(watch(), [file1.path, file2.path]); + checkProgramActualFiles(watch.getProgram(), [file1.path, libFile.path, file2.path]); + checkProgramRootFiles(watch.getProgram(), [file1.path, file2.path]); checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]); const configDir = getDirectoryPath(configFile.path); checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true); @@ -267,7 +254,7 @@ namespace ts.tscWatch { content: `{}` }; const host = createWatchedSystem([commonFile1, libFile, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); const configDir = getDirectoryPath(configFile.path); checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true); @@ -291,7 +278,7 @@ namespace ts.tscWatch { }` }; const host = createWatchedSystem([commonFile1, commonFile2, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); const commonFile3 = "/a/b/commonFile3.ts"; checkProgramRootFiles(watch(), [commonFile1.path, commonFile3]); @@ -304,7 +291,7 @@ namespace ts.tscWatch { content: `{}` }; const host = createWatchedSystem([commonFile1, commonFile2, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); // delete commonFile2 @@ -326,7 +313,7 @@ namespace ts.tscWatch { let x = y` }; const host = createWatchedSystem([file1, libFile]); - const watch = createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path], host); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, libFile.path]); @@ -352,7 +339,7 @@ namespace ts.tscWatch { }; const files = [commonFile1, commonFile2, configFile]; const host = createWatchedSystem(files); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); configFile.content = `{ @@ -379,7 +366,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([commonFile1, commonFile2, excludedFile1, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); }); @@ -407,7 +394,7 @@ namespace ts.tscWatch { }; const files = [file1, nodeModuleFile, classicModuleFile, configFile]; const host = createWatchedSystem(files); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, nodeModuleFile.path]); @@ -435,7 +422,7 @@ namespace ts.tscWatch { }` }; const host = createWatchedSystem([commonFile1, commonFile2, libFile, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [commonFile1.path, commonFile2.path]); }); @@ -453,7 +440,7 @@ namespace ts.tscWatch { content: `export let y = 1;` }; const host = createWatchedSystem([file1, file2, file3]); - const watch = createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path], host); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, file2.path]); @@ -482,7 +469,7 @@ namespace ts.tscWatch { content: `export let y = 1;` }; const host = createWatchedSystem([file1, file2, file3]); - const watch = createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path], host); checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); host.reloadFS([file1, file3]); @@ -505,7 +492,7 @@ namespace ts.tscWatch { content: `export let y = 1;` }; const host = createWatchedSystem([file1, file2, file3]); - const watch = createWatchModeWithoutConfigFile([file1.path, file3.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path, file3.path], host); checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); host.reloadFS([file1, file3]); @@ -533,7 +520,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file1, file2, file3, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramRootFiles(watch(), [file2.path, file3.path]); checkProgramActualFiles(watch(), [file1.path, file2.path, file3.path]); @@ -555,10 +542,10 @@ namespace ts.tscWatch { content: "export let y = 1;" }; const host = createWatchedSystem([file1, file2, file3]); - const watch = createWatchModeWithoutConfigFile([file2.path, file3.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file2.path, file3.path], host); checkProgramActualFiles(watch(), [file2.path, file3.path]); - const watch2 = createWatchModeWithoutConfigFile([file1.path], host); + const watch2 = createWatchOfFilesAndCompilerOptions([file1.path], host); checkProgramActualFiles(watch2(), [file1.path, file2.path, file3.path]); // Previous program shouldnt be updated @@ -581,7 +568,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file1, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), [file1.path]); host.reloadFS([file1, file2, configFile]); @@ -606,7 +593,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file1, file2, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), [file1.path]); @@ -636,7 +623,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file1, file2, configFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), [file1.path, file2.path]); const modifiedConfigFile = { @@ -664,7 +651,7 @@ namespace ts.tscWatch { content: JSON.stringify({ compilerOptions: {} }) }; const host = createWatchedSystem([file1, file2, libFile, config]); - const watch = createWatchModeWithConfigFile(config.path, host); + const watch = createWatchOfConfigFile(config.path, host); checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]); checkOutputErrors(host, emptyArray, /*isInitial*/ true); @@ -688,7 +675,7 @@ namespace ts.tscWatch { content: "{" }; const host = createWatchedSystem([file1, corruptedConfig]); - const watch = createWatchModeWithConfigFile(corruptedConfig.path, host); + const watch = createWatchOfConfigFile(corruptedConfig.path, host); checkProgramActualFiles(watch(), [file1.path]); }); @@ -738,7 +725,7 @@ namespace ts.tscWatch { }) }; const host = createWatchedSystem([libES5, libES2015Promise, app, config1], { executingFilePath: "/compiler/tsc.js" }); - const watch = createWatchModeWithConfigFile(config1.path, host); + const watch = createWatchOfConfigFile(config1.path, host); checkProgramActualFiles(watch(), [libES5.path, app.path]); @@ -763,7 +750,7 @@ namespace ts.tscWatch { }) }; const host = createWatchedSystem([f, config]); - const watch = createWatchModeWithConfigFile(config.path, host); + const watch = createWatchOfConfigFile(config.path, host); checkProgramActualFiles(watch(), [f.path]); }); @@ -777,7 +764,7 @@ namespace ts.tscWatch { content: 'import * as T from "./moduleFile"; T.bar();' }; const host = createWatchedSystem([moduleFile, file1, libFile]); - const watch = createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path], host); checkOutputErrors(host, emptyArray, /*isInitial*/ true); const moduleFileOldPath = moduleFile.path; @@ -809,7 +796,7 @@ namespace ts.tscWatch { content: `{}` }; const host = createWatchedSystem([moduleFile, file1, configFile, libFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkOutputErrors(host, emptyArray, /*isInitial*/ true); const moduleFileOldPath = moduleFile.path; @@ -844,7 +831,7 @@ namespace ts.tscWatch { path: "/a/c" }; const host = createWatchedSystem([f1, config, node, cwd], { currentDirectory: cwd.path }); - const watch = createWatchModeWithConfigFile(config.path, host); + const watch = createWatchOfConfigFile(config.path, host); checkProgramActualFiles(watch(), [f1.path, node.path]); }); @@ -859,7 +846,7 @@ namespace ts.tscWatch { content: 'import * as T from "./moduleFile"; T.bar();' }; const host = createWatchedSystem([file1, libFile]); - const watch = createWatchModeWithoutConfigFile([file1.path], host); + const watch = createWatchOfFilesAndCompilerOptions([file1.path], host); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), file1, "./moduleFile") @@ -886,7 +873,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkOutputErrors(host, [ getUnknownCompilerOption(watch(), configFile, "foo"), getUnknownCompilerOption(watch(), configFile, "allowJS") @@ -906,7 +893,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - createWatchModeWithConfigFile(configFile.path, host); + createWatchOfConfigFile(configFile.path, host); checkOutputErrors(host, emptyArray, /*isInitial*/ true); }); @@ -923,7 +910,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkOutputErrors(host, emptyArray, /*isInitial*/ true); configFile.content = `{ @@ -959,7 +946,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file1, configFile, libFile]); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), [libFile.path]); }); @@ -985,7 +972,7 @@ namespace ts.tscWatch { content: `export const x: number` }; const host = createWatchedSystem([f, config, t1, t2], { currentDirectory: getDirectoryPath(f.path) }); - const watch = createWatchModeWithConfigFile(config.path, host); + const watch = createWatchOfConfigFile(config.path, host); checkProgramActualFiles(watch(), [t1.path, t2.path]); }); @@ -996,7 +983,7 @@ namespace ts.tscWatch { content: "let x = 1" }; const host = createWatchedSystem([f, libFile]); - const watch = createWatchModeWithoutConfigFile([f.path], host, { allowNonTsExtensions: true }); + const watch = createWatchOfFilesAndCompilerOptions([f.path], host, { allowNonTsExtensions: true }); checkProgramActualFiles(watch(), [f.path, libFile.path]); }); @@ -1024,7 +1011,7 @@ namespace ts.tscWatch { const files = [file, libFile, configFile]; const host = createWatchedSystem(files); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); const errors = () => [ getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"allowJs"'), '"allowJs"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration"), getDiagnosticOfFile(watch().getCompilerOptions().configFile, configFile.content.indexOf('"declaration"'), '"declaration"'.length, Diagnostics.Option_0_cannot_be_specified_with_option_1, "allowJs", "declaration") @@ -1080,7 +1067,7 @@ namespace ts.tscWatch { const files = [f1, f2, config, libFile]; host.reloadFS(files); - createWatchModeWithConfigFile(config.path, host); + createWatchOfConfigFile(config.path, host); const allEmittedLines = getEmittedLines(files); checkOutputContains(host, allEmittedLines); @@ -1142,7 +1129,7 @@ namespace ts.tscWatch { mapOfFilesWritten.set(p, count ? count + 1 : 1); return originalWriteFile(p, content); }; - createWatchModeWithConfigFile(configFile.path, host); + createWatchOfConfigFile(configFile.path, host); if (useOutFile) { // Only out file assert.equal(mapOfFilesWritten.size, 1); @@ -1226,7 +1213,7 @@ namespace ts.tscWatch { host.reloadFS(firstReloadFileList ? getFiles(firstReloadFileList) : files); // Initial compile - createWatchModeWithConfigFile(configFile.path, host); + createWatchOfConfigFile(configFile.path, host); if (firstCompilationEmitFiles) { checkAffectedLines(host, getFiles(firstCompilationEmitFiles), allEmittedFiles); } @@ -1537,11 +1524,11 @@ namespace ts.tscWatch { // Initial compile if (configFile) { - createWatchModeWithConfigFile(configFile.path, host); + createWatchOfConfigFile(configFile.path, host); } else { // First file as the root - createWatchModeWithoutConfigFile([files[0].path], host, { listEmittedFiles: true }); + createWatchOfFilesAndCompilerOptions([files[0].path], host, { listEmittedFiles: true }); } checkOutputContains(host, allEmittedFiles); @@ -1661,7 +1648,7 @@ namespace ts.tscWatch { const files = [root, imported, libFile]; const host = createWatchedSystem(files); - const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD }); const f1IsNotModule = getDiagnosticOfFileFromProgram(watch(), root.path, root.content.indexOf('"f1"'), '"f1"'.length, Diagnostics.File_0_is_not_a_module, imported.path); const cannotFindFoo = getDiagnosticOfFileFromProgram(watch(), imported.path, imported.content.indexOf("foo"), "foo".length, Diagnostics.Cannot_find_name_0, "foo"); @@ -1762,7 +1749,7 @@ namespace ts.tscWatch { return originalFileExists.call(host, fileName); }; - const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD }); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); checkOutputErrors(host, [ @@ -1804,7 +1791,7 @@ namespace ts.tscWatch { return originalFileExists.call(host, fileName); }; - const watch = createWatchModeWithoutConfigFile([root.path], host, { module: ModuleKind.AMD }); + const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { module: ModuleKind.AMD }); assert.isTrue(fileExistsCalledForBar, "'fileExists' should be called"); checkOutputErrors(host, emptyArray, /*isInitial*/ true); @@ -1853,7 +1840,7 @@ declare module "fs" { const filesWithNodeType = files.concat(packageJson, nodeType); const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); - const watch = createWatchModeWithoutConfigFile([root.path], host, { }); + const watch = createWatchOfFilesAndCompilerOptions([root.path], host, { }); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") @@ -1895,7 +1882,7 @@ declare module "fs" { const files = [root, file, libFile]; const host = createWatchedSystem(files, { currentDirectory: "/a/b" }); - const watch = createWatchModeWithoutConfigFile([root.path, file.path], host, {}); + const watch = createWatchOfFilesAndCompilerOptions([root.path, file.path], host, {}); checkOutputErrors(host, [ getDiagnosticModuleNotFoundOfFile(watch(), root, "fs") @@ -1937,7 +1924,7 @@ declare module "fs" { const outDirFolder = "/a/b/projects/myProject/dist/"; const programFiles = [file1, file2, module1, libFile]; const host = createWatchedSystem(programFiles.concat(configFile), { currentDirectory: "/a/b/projects/myProject/" }); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), programFiles.map(f => f.path)); checkOutputErrors(host, emptyArray, /*isInitial*/ true); const expectedFiles: ExpectedFile[] = [ @@ -2014,7 +2001,7 @@ declare module "fs" { }; const files = [configFile, file1, file2, libFile]; const host = createWatchedSystem(files); - const watch = createWatchModeWithConfigFile(configFile.path, host); + const watch = createWatchOfConfigFile(configFile.path, host); checkProgramActualFiles(watch(), mapDefined(files, f => f === configFile ? undefined : f.path)); file1.content = "var zz30 = 100;"; diff --git a/src/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index d4d203cabbb..f2a178610c9 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -460,7 +460,7 @@ interface Array {}` private invokeFileWatcher(fileFullPath: string, eventKind: FileWatcherEventKind) { const callbacks = this.watchedFiles.get(this.toPath(fileFullPath)); - invokeWatcherCallbacks(callbacks, ({ cb, fileName }) => cb(fileName, eventKind)); + invokeWatcherCallbacks(callbacks, ({ cb }) => cb(fileFullPath, eventKind)); } private getRelativePathToDirectory(directoryFullPath: string, fileFullPath: string) { diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index d73014a93a2..ba944bafd9d 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -1,4 +1,4 @@ -{ +{ "extends": "../tsconfig-base", "compilerOptions": { "removeComments": false, @@ -37,6 +37,10 @@ "../compiler/declarationEmitter.ts", "../compiler/emitter.ts", "../compiler/program.ts", + "../compiler/builder.ts", + "../compiler/resolutionCache.ts", + "../compiler/watch.ts", + "../compiler/watchUtilities.ts", "../compiler/commandLineParser.ts", "../compiler/diagnosticInformationMap.generated.ts", "types.ts", diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5dec286a8b7..fb5ee37012d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3812,6 +3812,72 @@ declare namespace ts { */ function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; } +declare namespace ts { + type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** + * Creates the function that compiles the program by maintaining the builder state and also return diagnostic reporter + */ + function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; + interface WatchHost { + /** FS system to use */ + system: System; + /** Custom action before creating the program */ + beforeProgramCreate(compilerOptions: CompilerOptions): void; + /** Custom action after new program creation is successful */ + afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + } + /** + * Host to create watch with root files and options + */ + interface WatchOfFilesAndCompilerOptionsHost extends WatchHost { + /** root files to use to generate program */ + rootFiles: string[]; + /** Compiler options */ + options: CompilerOptions; + } + /** + * Host to create watch with config file + */ + interface WatchOfConfigFileHost extends WatchHost { + /** Name of the config file to compile */ + configFileName: string; + /** Options to extend */ + optionsToExtend?: CompilerOptions; + onConfigFileDiagnostic(diagnostic: Diagnostic): void; + } + interface Watch { + /** Synchronize the program with the changes */ + synchronizeProgram(): void; + } + /** + * Creates the watch what generates program using the config file + */ + interface WatchOfConfigFile extends Watch { + } + /** + * Creates the watch that generates program using the root files and compiler options + */ + interface WatchOfFilesAndCompilerOptions extends Watch { + /** Updates the root files in the program, only if this is not config file compilation */ + updateRootFileNames(fileNames: string[]): void; + } + /** + * Create the watched program for config file + */ + function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile; + /** + * Create the watched program for root files and compiler options + */ + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions; + /** + * Creates the watch from the host for root files and compiler options + */ + function createWatch(host: WatchOfFilesAndCompilerOptionsHost): WatchOfFilesAndCompilerOptions; + /** + * Creates the watch from the host for config file + */ + function createWatch(host: WatchOfConfigFileHost): WatchOfConfigFile; +} declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; /** From 7ebf9d9f9d76e4a655d0079a7663a574842fa373 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Nov 2017 11:35:38 -0800 Subject: [PATCH 003/100] Lint errors fix --- src/compiler/builder.ts | 8 ++++++-- src/harness/unittests/builder.ts | 4 +++- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 9a60557ae36..0881f68800c 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -166,12 +166,16 @@ namespace ts { semanticDiagnosticsPerFile.delete(sourceFile.path); } - newReferences && referencedMap.set(sourceFile.path, newReferences); + if (newReferences) { + referencedMap.set(sourceFile.path, newReferences); + } fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); } // For removed files, remove the semantic diagnostics removed files as changed - useOldState && oldState.fileInfos.forEach((_value, path) => !fileInfos.has(path) && semanticDiagnosticsPerFile.delete(path)); + if (useOldState) { + oldState.fileInfos.forEach((_value, path) => !fileInfos.has(path) && semanticDiagnosticsPerFile.delete(path)); + } // Set the old state and program to undefined to ensure we arent keeping them alive hence forward oldState = undefined; diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index 60297d2b7fc..89b0852bd32 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -53,7 +53,9 @@ namespace ts { const program = getProgram(); builderState = createBuilderState(program, builderOptions, builderState); const outputFileNames: string[] = []; - while (builderState.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { } + // tslint:disable-next-line no-empty + while (builderState.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { + } assert.deepEqual(outputFileNames, fileNames); }; } From 3c5a6e1ae7848d97e7178a4d3e8043cf33d15fca Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 7 Nov 2017 13:08:20 -0800 Subject: [PATCH 004/100] Allow watch host to specify module name resolver --- src/compiler/watch.ts | 10 ++++++++-- tests/baselines/reference/api/typescript.d.ts | 2 ++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 0ff0d2f6122..ff352c0febc 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -170,6 +170,9 @@ namespace ts { beforeProgramCreate(compilerOptions: CompilerOptions): void; /** Custom action after new program creation is successful */ afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + + /** Optional module name resolver */ + moduleNameResolver?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** @@ -308,6 +311,9 @@ namespace ts { const getCachedDirectoryStructureHost = configFileName && (() => directoryStructureHost as CachedDirectoryStructureHost); const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames); let newLine = getNewLineCharacter(compilerOptions, system); + const resolveModuleNames: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModule[] = host.moduleNameResolver ? + (moduleNames, containingFile, reusedNames) => host.moduleNameResolver(moduleNames, containingFile, reusedNames) : + (moduleNames, containingFile, reusedNames?) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false); const compilerHost: CompilerHost & ResolutionCacheHost = { // Members for CompilerHost @@ -328,7 +334,7 @@ namespace ts { getDirectories: path => directoryStructureHost.getDirectories(path), realpath, resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile), - resolveModuleNames: (moduleNames, containingFile, reusedNames?) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false), + resolveModuleNames, onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, @@ -341,7 +347,7 @@ namespace ts { hasChangedAutomaticTypeDirectiveNames = true; scheduleProgramUpdate(); }, - writeLog + writeLog, }; // Cache for the module resolution const resolutionCache = createResolutionCache(compilerHost, configFileName ? diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index fb5ee37012d..63e2d453b18 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3825,6 +3825,8 @@ declare namespace ts { beforeProgramCreate(compilerOptions: CompilerOptions): void; /** Custom action after new program creation is successful */ afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + /** Optional module name resolver */ + moduleNameResolver?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** * Host to create watch with root files and options From c9a17f325b96f812604b529ce2ba36d21e3441b8 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 9 Nov 2017 13:35:56 -0800 Subject: [PATCH 005/100] Add api to get the dependencies of the file --- src/compiler/builder.ts | 54 ++++++++++++++++++- .../reference/api/tsserverlibrary.d.ts | 4 ++ tests/baselines/reference/api/typescript.d.ts | 4 ++ 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 0881f68800c..efa0f0a0372 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -93,6 +93,11 @@ namespace ts { * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files */ getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + + /** + * Get all the dependencies of the file + */ + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; } /** @@ -190,7 +195,8 @@ namespace ts { canCreateNewStateFrom, getFilesAffectedBy, emitNextAffectedFile, - getSemanticDiagnostics + getSemanticDiagnostics, + getAllDependencies }; /** @@ -306,6 +312,52 @@ namespace ts { return diagnostics; } + /** + * Get all the dependencies of the sourceFile + */ + function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[] { + const compilerOptions = programOfThisState.getCompilerOptions(); + // With --out or --outFile all outputs go into single file, all files depend on each other + if (compilerOptions.outFile || compilerOptions.out) { + return programOfThisState.getSourceFiles().map(getFileName); + } + + // If this is non module emit, or its a global file, it depends on all the source files + if (!isModuleEmit || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { + return programOfThisState.getSourceFiles().map(getFileName); + } + + // Get the references, traversing deep from the referenceMap + Debug.assert(!!referencedMap); + const seenMap = createMap(); + const queue = [sourceFile.path]; + while (queue.length) { + const path = queue.pop(); + if (!seenMap.has(path)) { + seenMap.set(path, true); + const references = referencedMap.get(path); + if (references) { + const iterator = references.keys(); + for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { + queue.push(value as Path); + } + } + } + } + + return flatMapIter(seenMap.keys(), path => { + const file = programOfThisState.getSourceFileByPath(path as Path); + if (file) { + return file.fileName; + } + return path; + }); + } + + function getFileName(sourceFile: SourceFile) { + return sourceFile.fileName; + } + /** * For script files that contains only ambient external modules, although they are not actually external module files, * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 3ec16254780..5c306474533 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3815,6 +3815,10 @@ declare namespace ts { * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files */ getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get all the dependencies of the file + */ + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; } /** * Information about the source file: Its version and optional signature from last emit diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 63e2d453b18..2212a859c0a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3762,6 +3762,10 @@ declare namespace ts { * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files */ getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get all the dependencies of the file + */ + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; } /** * Information about the source file: Its version and optional signature from last emit From 6d36a3d778b188a29cef1271cb24e270d93d644b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 14 Nov 2017 11:35:20 -0800 Subject: [PATCH 006/100] Make the versions in the source file non zero when the source file is created --- src/compiler/watch.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index ff352c0febc..07fb9203b6a 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -468,9 +468,9 @@ namespace ts { else { let fileWatcher: FileWatcher; if (sourceFile) { - sourceFile.version = "0"; + sourceFile.version = "1"; fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); - sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher }); + sourceFilesCache.set(path, { sourceFile, version: 1, fileWatcher }); } else { sourceFilesCache.set(path, "0"); @@ -612,7 +612,7 @@ namespace ts { resolutionCache.invalidateResolutionOfFile(path); if (!isString(hostSourceFile)) { hostSourceFile.fileWatcher.close(); - sourceFilesCache.set(path, (hostSourceFile.version++).toString()); + sourceFilesCache.set(path, (++hostSourceFile.version).toString()); } } else { From 85ce1d0398e252192b1826b9c2818a091b783f3e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 14 Nov 2017 14:47:58 -0800 Subject: [PATCH 007/100] Make the builder state as internal and expose builder instead of builder state --- src/compiler/builder.ts | 627 ++++++++++++------ src/compiler/watch.ts | 15 +- src/harness/unittests/builder.ts | 9 +- src/server/project.ts | 19 +- .../reference/api/tsserverlibrary.d.ts | 85 ++- tests/baselines/reference/api/typescript.d.ts | 85 ++- 6 files changed, 521 insertions(+), 319 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index efa0f0a0372..8703d83aa1d 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1,29 +1,56 @@ /// +/*@internal*/ namespace ts { - export interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } - - export interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } - - /* @internal */ export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput { const outputFiles: OutputFile[] = []; const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); - return { outputFiles, emitSkipped: emitResult.emitSkipped }; + return { outputFiles, emitSkipped: emitResult.emitSkipped }; function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { outputFiles.push({ name: fileName, writeByteOrderMark, text }); } } + /** + * Internal Builder to get files affected by another file + */ + export interface InternalBuilder extends BaseBuilder { + /** + * Gets the files affected by the file path + * This api is only for internal use + */ + /*@internal*/ + getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray; + } + + /** + * Create the internal builder to get files affected by sourceFile + */ + export function createInternalBuilder(options: BuilderOptions): InternalBuilder { + return createBuilder(options, BuilderType.InternalBuilder); + } + + export enum BuilderType { + InternalBuilder, + SemanticDiagnosticsBuilder, + EmitAndSemanticDiagnosticsBuilder + } + + /** + * Information about the source file: Its version and optional signature from last emit + */ + interface FileInfo { + version: string; + signature?: string; + } + + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + type ReferencedSet = ReadonlyMap; + function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { if (map1 === undefined) { return map2 === undefined; @@ -35,191 +62,273 @@ namespace ts { return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); } - /** - * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) - * Note that it is only safe to pass BuilderState as old state when creating new state, when - * - If iterator's next method to get next affected file is never called - * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete - */ - export interface BuilderState { + export function createBuilder(options: BuilderOptions, builderType: BuilderType.InternalBuilder): InternalBuilder; + export function createBuilder(options: BuilderOptions, builderType: BuilderType.SemanticDiagnosticsBuilder): SemanticDiagnosticsBuilder; + export function createBuilder(options: BuilderOptions, builderType: BuilderType.EmitAndSemanticDiagnosticsBuilder): EmitAndSemanticDiagnosticsBuilder; + export function createBuilder(options: BuilderOptions, builderType: BuilderType) { /** - * The map of file infos, where there is entry for each file in the program - * The entry is signature of the file (from last emit) or empty string + * Information of the file eg. its version, signature etc */ - fileInfos: ReadonlyMap>; - - /** - * Returns true if module gerneration is not ModuleKind.None - */ - isModuleEmit: boolean; - - /** - * Map of file referenced or undefined if it wasnt module emit - * The entry is present only if file references other files - * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) - */ - referencedMap: ReadonlyMap | undefined; - - /** - * Set of source file's paths that have been changed, either in resolution or versions - */ - changedFilesSet: ReadonlyMap; - - /** - * Set of cached semantic diagnostics per file - */ - semanticDiagnosticsPerFile: ReadonlyMap>; - - /** - * Returns true if this state is safe to use as oldState - */ - canCreateNewStateFrom(): boolean; - - /** - * Gets the files affected by the file path - * This api is only for internal use - */ - /* @internal */ - getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray; - - /** - * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete - */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; - - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; - - /** - * Get all the dependencies of the file - */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; - } - - /** - * Information about the source file: Its version and optional signature from last emit - */ - export interface FileInfo { - version: string; - signature: string; - } - - export interface AffectedFileEmitResult extends EmitResult { - affectedFile?: SourceFile; - } - - /** - * Referenced files with values for the keys as referenced file's path to be true - */ - export type ReferencedSet = ReadonlyMap; - - export interface BuilderOptions { - getCanonicalFileName: GetCanonicalFileName; - computeHash: (data: string) => string; - } - - export function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState { const fileInfos = createMap(); - const isModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; - const referencedMap = isModuleEmit ? createMap() : undefined; + /** + * true if module emit is enabled + */ + let isModuleEmit: boolean; + + /** + * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled + * Otherwise undefined + */ + let referencedMap: Map | undefined; + + /** + * Get the files affected by the source file. + * This is dependent on whether its a module emit or not and hence function expression + */ + let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map | undefined) => ReadonlyArray; + + /** + * Cache of semantic diagnostics for files with their Path being the key + */ const semanticDiagnosticsPerFile = createMap>(); - /** The map has key by source file's path that has been changed */ + + /** + * The map has key by source file's path that has been changed + */ const changedFilesSet = createMap(); - const hasShapeChanged = createMap(); + + /** + * Map of files that have already called update signature. + * That means hence forth these files are assumed to have + * no change in their signature for this version of the program + */ + const hasCalledUpdateShapeSignature = createMap(); + + /** + * Cache of all files excluding default library file for the current program + */ let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; - // Iterator datas + /** + * Set of affected files being iterated + */ let affectedFiles: ReadonlyArray | undefined; + /** + * Current index to retrieve affected file from + */ let affectedFilesIndex = 0; + /** + * Current changed file for iterating over affected files + */ + let currentChangedFilePath: Path | undefined; + /** + * Map of file signatures, with key being file path, calculated while getting current changed file's affected files + * These will be commited whenever the iteration through affected files of current changed file is complete + */ + const currentAffectedFilesSignatures = createMap(); + /** + * Already seen affected files + */ const seenAffectedFiles = createMap(); - const getEmitDependentFilesAffectedBy = isModuleEmit ? - getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit; - const useOldState = oldState && oldState.isModuleEmit === isModuleEmit; - if (useOldState) { - Debug.assert(oldState.canCreateNewStateFrom(), "Cannot use this state as old state"); - Debug.assert(!forEachEntry(oldState.changedFilesSet, (_value, path) => oldState.semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); - - copyEntries(oldState.changedFilesSet, changedFilesSet); - copyEntries(oldState.semanticDiagnosticsPerFile, semanticDiagnosticsPerFile); + switch (builderType) { + case BuilderType.InternalBuilder: + return getInternalBuilder(); + case BuilderType.SemanticDiagnosticsBuilder: + return getSemanticDiagnosticsBuilder(); + case BuilderType.EmitAndSemanticDiagnosticsBuilder: + return getEmitAndSemanticDiagnosticsBuilder(); + default: + notImplemented(); } - for (const sourceFile of newProgram.getSourceFiles()) { - const version = sourceFile.version; - let oldInfo: Readonly; - let oldReferences: ReferencedSet; - const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); - - // Register changed file - // if not using old state so every file is changed - if (!useOldState || - // File wasnt present earlier - !(oldInfo = oldState.fileInfos.get(sourceFile.path)) || - // versions dont match - oldInfo.version !== version || - // Referenced files changed - !hasSameKeys(newReferences, (oldReferences = oldState.referencedMap && oldState.referencedMap.get(sourceFile.path))) || - // Referenced file was deleted - newReferences && forEachEntry(newReferences, (_value, path) => oldState.fileInfos.has(path) && !newProgram.getSourceFileByPath(path as Path))) { - changedFilesSet.set(sourceFile.path, true); - // All changed files need to re-evaluate its semantic diagnostics - semanticDiagnosticsPerFile.delete(sourceFile.path); - } - - if (newReferences) { - referencedMap.set(sourceFile.path, newReferences); - } - fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); + function getInternalBuilder(): InternalBuilder { + return { + updateProgram, + getFilesAffectedBy, + getAllDependencies + }; } - // For removed files, remove the semantic diagnostics removed files as changed - if (useOldState) { - oldState.fileInfos.forEach((_value, path) => !fileInfos.has(path) && semanticDiagnosticsPerFile.delete(path)); + function getSemanticDiagnosticsBuilder(): SemanticDiagnosticsBuilder { + return { + updateProgram, + getAllDependencies, + getSemanticDiagnosticsOfNextAffectedFile, + getSemanticDiagnostics + }; } - // Set the old state and program to undefined to ensure we arent keeping them alive hence forward - oldState = undefined; - newProgram = undefined; - - return { - fileInfos, - isModuleEmit, - referencedMap, - changedFilesSet, - semanticDiagnosticsPerFile, - canCreateNewStateFrom, - getFilesAffectedBy, - emitNextAffectedFile, - getSemanticDiagnostics, - getAllDependencies - }; + function getEmitAndSemanticDiagnosticsBuilder(): EmitAndSemanticDiagnosticsBuilder { + return { + updateProgram, + getAllDependencies, + emitNextAffectedFile, + getSemanticDiagnostics + }; + } /** - * Can use this state as old State if we have iterated through all affected files present + * Update current state to reflect new program + * Updates changed files, references, file infos etc */ - function canCreateNewStateFrom() { - return !affectedFiles || affectedFiles.length <= affectedFilesIndex; + function updateProgram(newProgram: Program) { + const newProgramHasModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; + const oldReferencedMap = referencedMap; + if (isModuleEmit !== newProgramHasModuleEmit) { + // Changes in the module emit, clear out everything and initialize as if first time + + // Clear file information and semantic diagnostics + fileInfos.clear(); + semanticDiagnosticsPerFile.clear(); + + // Clear changed files and affected files information + changedFilesSet.clear(); + affectedFiles = undefined; + currentChangedFilePath = undefined; + currentAffectedFilesSignatures.clear(); + + // Update the reference map creation + referencedMap = newProgramHasModuleEmit ? createMap() : undefined; + + // Update the module emit + isModuleEmit = newProgramHasModuleEmit; + getEmitDependentFilesAffectedBy = isModuleEmit ? + getFilesAffectedByUpdatedShapeWhenModuleEmit : + getFilesAffectedByUpdatedShapeWhenNonModuleEmit; + } + else { + if (currentChangedFilePath) { + // Remove the diagnostics for all the affected files since we should resume the state such that + // the whole iteration on currentChangedFile never happened + affectedFiles.map(sourceFile => semanticDiagnosticsPerFile.delete(sourceFile.path)); + affectedFiles = undefined; + currentAffectedFilesSignatures.clear(); + } + else { + // Verify the sanity of old state + Debug.assert(!affectedFiles && !currentAffectedFilesSignatures.size, "Cannot reuse if only few affected files of currentChangedFile were iterated"); + } + Debug.assert(!forEachEntry(changedFilesSet, (_value, path) => semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); + } + + // Clear datas that cant be retained beyond previous state + seenAffectedFiles.clear(); + hasCalledUpdateShapeSignature.clear(); + allFilesExcludingDefaultLibraryFile = undefined; + + // Create the reference map and update changed files + for (const sourceFile of newProgram.getSourceFiles()) { + const version = sourceFile.version; + const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); + const oldInfo = fileInfos.get(sourceFile.path); + let oldReferences: ReferencedSet; + + // Register changed file if its new file or we arent reusing old state + if (!oldInfo) { + // New file: Set the file info + fileInfos.set(sourceFile.path, { version }); + changedFilesSet.set(sourceFile.path, true); + } + // versions dont match + else if (oldInfo.version !== version || + // Referenced files changed + !hasSameKeys(newReferences, (oldReferences = oldReferencedMap && oldReferencedMap.get(sourceFile.path))) || + // Referenced file was deleted in the new program + newReferences && forEachEntry(newReferences, (_value, path) => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { + + // Changed file: Update the version, set as changed file + oldInfo.version = version; + changedFilesSet.set(sourceFile.path, true); + + // All changed files need to re-evaluate its semantic diagnostics + semanticDiagnosticsPerFile.delete(sourceFile.path); + } + + // Set the references + if (newReferences) { + referencedMap.set(sourceFile.path, newReferences); + } + else if (referencedMap) { + referencedMap.delete(sourceFile.path); + } + } + + // For removed files, remove the semantic diagnostics and file info + if (fileInfos.size > newProgram.getSourceFiles().length) { + fileInfos.forEach((_value, path) => { + if (!newProgram.getSourceFileByPath(path as Path)) { + fileInfos.delete(path); + semanticDiagnosticsPerFile.delete(path); + if (referencedMap) { + referencedMap.delete(path); + } + } + }); + } } /** * Gets the files affected by the path from the program */ - function getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray { + function getFilesAffectedBy(programOfThisState: Program, path: Path, cacheToUpdateSignature?: Map): ReadonlyArray { const sourceFile = programOfThisState.getSourceFileByPath(path); if (!sourceFile) { return emptyArray; } - if (!updateShapeSignature(programOfThisState, sourceFile)) { + if (!updateShapeSignature(programOfThisState, sourceFile, cacheToUpdateSignature)) { return [sourceFile]; } - return getEmitDependentFilesAffectedBy(programOfThisState, sourceFile); + return getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, cacheToUpdateSignature); + } + + function getNextAffectedFile(programOfThisState: Program): SourceFile | Program | undefined { + while (true) { + if (affectedFiles) { + while (affectedFilesIndex < affectedFiles.length) { + const affectedFile = affectedFiles[affectedFilesIndex]; + affectedFilesIndex++; + if (!seenAffectedFiles.has(affectedFile.path)) { + // Set the next affected file as seen and remove the cached semantic diagnostics + seenAffectedFiles.set(affectedFile.path, true); + semanticDiagnosticsPerFile.delete(affectedFile.path); + return affectedFile; + } + } + + // Remove the changed file from the change set + changedFilesSet.delete(currentChangedFilePath); + currentChangedFilePath = undefined; + // Commit the changes in file signature + currentAffectedFilesSignatures.forEach((signature, path) => fileInfos.get(path).signature = signature); + currentAffectedFilesSignatures.clear(); + affectedFiles = undefined; + } + + // Get next changed file + const nextKey = changedFilesSet.keys().next(); + if (nextKey.done) { + // Done + return undefined; + } + + const compilerOptions = programOfThisState.getCompilerOptions(); + // With --out or --outFile all outputs go into single file + // so operations are performed directly on program, return program + if (compilerOptions.outFile || compilerOptions.out) { + Debug.assert(semanticDiagnosticsPerFile.size === 0); + changedFilesSet.clear(); + return programOfThisState; + } + + // Get next batch of affected files + currentChangedFilePath = nextKey.value as Path; + affectedFilesIndex = 0; + affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path, currentAffectedFilesSignatures); + } } /** @@ -227,47 +336,48 @@ namespace ts { * Returns undefined when iteration is complete */ function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined { - if (affectedFiles) { - while (affectedFilesIndex < affectedFiles.length) { - const affectedFile = affectedFiles[affectedFilesIndex]; - affectedFilesIndex++; - if (!seenAffectedFiles.has(affectedFile.path)) { - seenAffectedFiles.set(affectedFile.path, true); - - // Emit the affected file - const result = programOfThisState.emit(affectedFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers) as AffectedFileEmitResult; - result.affectedFile = affectedFile; - return result; - } - } - - affectedFiles = undefined; - } - - // Get next changed file - const nextKey = changedFilesSet.keys().next(); - if (nextKey.done) { + const affectedFile = getNextAffectedFile(programOfThisState); + if (!affectedFile) { // Done return undefined; } - - const compilerOptions = programOfThisState.getCompilerOptions(); - // With --out or --outFile all outputs go into single file, do it only once - if (compilerOptions.outFile || compilerOptions.out) { - Debug.assert(semanticDiagnosticsPerFile.size === 0); - changedFilesSet.clear(); + else if (affectedFile === programOfThisState) { + // When whole program is affected, do emit only once (eg when --out or --outFile is specified) return programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers); } - // Get next batch of affected files - changedFilesSet.delete(nextKey.value); - affectedFilesIndex = 0; - affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path); + // Emit the affected file + const targetSourceFile = affectedFile as SourceFile; + const result = programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers) as AffectedFileEmitResult; + result.affectedFile = targetSourceFile; + return result; + } - // Clear the semantic diagnostic of affected files - affectedFiles.forEach(affectedFile => semanticDiagnosticsPerFile.delete(affectedFile.path)); + /** + * Return the semantic diagnostics for the next affected file or undefined if iteration is complete + * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true + */ + function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray { + while (true) { + const affectedFile = getNextAffectedFile(programOfThisState); + if (!affectedFile) { + // Done + return undefined; + } + else if (affectedFile === programOfThisState) { + // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) + return programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken); + } - return emitNextAffectedFile(programOfThisState, writeFileCallback, cancellationToken, customTransformers); + // Get diagnostics for the affected file if its not ignored + const targetSourceFile = affectedFile as SourceFile; + if (ignoreSourceFile && ignoreSourceFile(targetSourceFile)) { + // Get next affected file + continue; + } + + return getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken); + } } /** @@ -276,6 +386,7 @@ namespace ts { * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files */ function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { + Debug.assert(!affectedFiles || affectedFiles[affectedFilesIndex - 1] !== sourceFile || !semanticDiagnosticsPerFile.has(sourceFile.path)); const compilerOptions = programOfThisState.getCompilerOptions(); if (compilerOptions.outFile || compilerOptions.out) { Debug.assert(semanticDiagnosticsPerFile.size === 0); @@ -373,19 +484,20 @@ namespace ts { return true; } - /** - * Returns if the shape of the signature has changed since last emit - * Note that it also updates the current signature as the latest signature for the file - */ - function updateShapeSignature(program: Program, sourceFile: SourceFile) { + /** + * Returns if the shape of the signature has changed since last emit + * Note that it also updates the current signature as the latest signature for the file + */ + function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map | undefined) { Debug.assert(!!sourceFile); // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate - if (hasShapeChanged.has(sourceFile.path)) { + if (hasCalledUpdateShapeSignature.has(sourceFile.path)) { return false; } - hasShapeChanged.set(sourceFile.path, true); + Debug.assert(!cacheToUpdateSignature || !cacheToUpdateSignature.has(sourceFile.path)); + hasCalledUpdateShapeSignature.set(sourceFile.path, true); const info = fileInfos.get(sourceFile.path); Debug.assert(!!info); @@ -393,13 +505,13 @@ namespace ts { let latestSignature: string; if (sourceFile.isDeclarationFile) { latestSignature = sourceFile.version; - info.signature = latestSignature; + setLatestSigature(); } else { const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true); if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { latestSignature = options.computeHash(emitOutput.outputFiles[0].text); - info.signature = latestSignature; + setLatestSigature(); } else { latestSignature = prevSignature; @@ -407,6 +519,15 @@ namespace ts { } return !prevSignature || latestSignature !== prevSignature; + + function setLatestSigature() { + if (cacheToUpdateSignature) { + cacheToUpdateSignature.set(sourceFile.path, latestSignature); + } + else { + info.signature = latestSignature; + } + } } /** @@ -514,7 +635,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map | undefined) { if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); } @@ -537,7 +658,7 @@ namespace ts { if (!seenFileNamesMap.has(currentPath)) { const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile)) { + if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature)) { queue.push(...getReferencedByPaths(currentPath)); } } @@ -548,3 +669,93 @@ namespace ts { } } } + +namespace ts { + export interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + + export interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } + + export interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + + export interface BuilderOptions { + getCanonicalFileName: (fileName: string) => string; + computeHash: (data: string) => string; + } + + /** + * Builder to manage the program state changes + */ + export interface BaseBuilder { + /** + * Updates the program in the builder to represent new state + */ + updateProgram(newProgram: Program): void; + + /** + * Get all the dependencies of the file + */ + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; + } + + /** + * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files + */ + export interface SemanticDiagnosticsBuilder extends BaseBuilder { + /** + * Gets the semantic diagnostics from the program for the next affected file and caches it + * Returns undefined if the iteration is complete + */ + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; + + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics + */ + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + + /** + * The builder that can handle the changes in program and iterate through changed file to emit the files + * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files + */ + export interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { + /** + * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + */ + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics + */ + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + + /** + * Create the builder to manage semantic diagnostics and cache them + */ + export function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder { + return createBuilder(options, BuilderType.SemanticDiagnosticsBuilder); + } + + /** + * Create the builder that can handle the changes in program and iterate through changed files + * to emit the those files and manage semantic diagnostics cache as well + */ + export function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder { + return createBuilder(options, BuilderType.EmitAndSemanticDiagnosticsBuilder); + } +} diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 07fb9203b6a..fcb4d98ace0 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -84,18 +84,17 @@ namespace ts { } /** - * Creates the function that compiles the program by maintaining the builder state and also return diagnostic reporter + * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files */ export function createProgramCompilerWithBuilderState(system = sys, reportDiagnostic?: DiagnosticReporter) { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - let builderState: Readonly | undefined; - const options: BuilderOptions = { + const builder = createEmitAndSemanticDiagnosticsBuilder({ getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), - computeHash: data => system.createHash ? system.createHash(data) : data - }; + computeHash: system.createHash ? system.createHash.bind(system) : identity + }); return (host: DirectoryStructureHost, program: Program) => { - builderState = createBuilderState(program, options, builderState); + builder.updateProgram(program); // First get and report any syntactic errors. const diagnostics = program.getSyntacticDiagnostics().slice(); @@ -118,14 +117,14 @@ namespace ts { let emitSkipped: boolean; let affectedEmitResult: AffectedFileEmitResult; - while (affectedEmitResult = builderState.emitNextAffectedFile(program, writeFile)) { + while (affectedEmitResult = builder.emitNextAffectedFile(program, writeFile)) { emitSkipped = emitSkipped || affectedEmitResult.emitSkipped; addRange(diagnostics, affectedEmitResult.diagnostics); sourceMaps = addRange(sourceMaps, affectedEmitResult.sourceMaps); } if (reportSemanticDiagnostics) { - addRange(diagnostics, builderState.getSemanticDiagnostics(program)); + addRange(diagnostics, builder.getSemanticDiagnostics(program)); } sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index 89b0852bd32..a9c16c59fbd 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -44,17 +44,16 @@ namespace ts { }); function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { - let builderState: BuilderState; - const builderOptions: BuilderOptions = { + const builder = createEmitAndSemanticDiagnosticsBuilder({ getCanonicalFileName: identity, computeHash: identity - }; + }); return fileNames => { const program = getProgram(); - builderState = createBuilderState(program, builderOptions, builderState); + builder.updateProgram(program); const outputFileNames: string[] = []; // tslint:disable-next-line no-empty - while (builderState.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { + while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { } assert.deepEqual(outputFileNames, fileNames); }; diff --git a/src/server/project.ts b/src/server/project.ts index 056fedf19af..3a5785163d2 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -139,7 +139,7 @@ namespace ts.server { /*@internal*/ resolutionCache: ResolutionCache; - private builderState: BuilderState; + private builder: InternalBuilder | undefined; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -451,11 +451,14 @@ namespace ts.server { return []; } this.updateGraph(); - this.builderState = createBuilderState(this.program, { - getCanonicalFileName: this.projectService.toCanonicalFileName, - computeHash: data => this.projectService.host.createHash(data) - }, this.builderState); - return mapDefined(this.builderState.getFilesAffectedBy(this.program, scriptInfo.path), + if (!this.builder) { + this.builder = createInternalBuilder({ + getCanonicalFileName: this.projectService.toCanonicalFileName, + computeHash: data => this.projectService.host.createHash(data) + }); + } + this.builder.updateProgram(this.program); + return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined); } @@ -491,7 +494,7 @@ namespace ts.server { } this.languageService.cleanupSemanticCache(); this.languageServiceEnabled = false; - this.builderState = undefined; + this.builder = undefined; this.resolutionCache.closeTypeRootsWatch(); this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false); } @@ -532,7 +535,7 @@ namespace ts.server { this.rootFilesMap = undefined; this.externalFiles = undefined; this.program = undefined; - this.builderState = undefined; + this.builder = undefined; this.resolutionCache.clear(); this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 5c306474533..e258d179310 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3771,40 +3771,48 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } + interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + interface BuilderOptions { + getCanonicalFileName: (fileName: string) => string; + computeHash: (data: string) => string; + } /** - * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) - * Note that it is only safe to pass BuilderState as old state when creating new state, when - * - If iterator's next method to get next affected file is never called - * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete + * Builder to manage the program state changes */ - interface BuilderState { + interface BaseBuilder { /** - * The map of file infos, where there is entry for each file in the program - * The entry is signature of the file (from last emit) or empty string + * Updates the program in the builder to represent new state */ - fileInfos: ReadonlyMap>; + updateProgram(newProgram: Program): void; /** - * Returns true if module gerneration is not ModuleKind.None + * Get all the dependencies of the file */ - isModuleEmit: boolean; + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; + } + /** + * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files + */ + interface SemanticDiagnosticsBuilder extends BaseBuilder { /** - * Map of file referenced or undefined if it wasnt module emit - * The entry is present only if file references other files - * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) + * Gets the semantic diagnostics from the program for the next affected file and caches it + * Returns undefined if the iteration is complete */ - referencedMap: ReadonlyMap | undefined; + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; /** - * Set of source file's paths that have been changed, either in resolution or versions + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics */ - changedFilesSet: ReadonlyMap; - /** - * Set of cached semantic diagnostics per file - */ - semanticDiagnosticsPerFile: ReadonlyMap>; - /** - * Returns true if this state is safe to use as oldState - */ - canCreateNewStateFrom(): boolean; + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + /** + * The builder that can handle the changes in program and iterate through changed file to emit the files + * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files + */ + interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete */ @@ -3812,33 +3820,20 @@ declare namespace ts { /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics */ getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; - /** - * Get all the dependencies of the file - */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; } /** - * Information about the source file: Its version and optional signature from last emit + * Create the builder to manage semantic diagnostics and cache them */ - interface FileInfo { - version: string; - signature: string; - } - interface AffectedFileEmitResult extends EmitResult { - affectedFile?: SourceFile; - } + function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder; /** - * Referenced files with values for the keys as referenced file's path to be true + * Create the builder that can handle the changes in program and iterate through changed files + * to emit the those files and manage semantic diagnostics cache as well */ - type ReferencedSet = ReadonlyMap; - interface BuilderOptions { - getCanonicalFileName: (fileName: string) => string; - computeHash: (data: string) => string; - } - function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState; + function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; @@ -7278,7 +7273,7 @@ declare namespace ts.server { languageServiceEnabled: boolean; readonly trace?: (s: string) => void; readonly realpath?: (path: string) => string; - private builderState; + private builder; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 2212a859c0a..bac53072302 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3718,40 +3718,48 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } + interface AffectedFileEmitResult extends EmitResult { + affectedFile?: SourceFile; + } + interface BuilderOptions { + getCanonicalFileName: (fileName: string) => string; + computeHash: (data: string) => string; + } /** - * State on which you can query affected files (files to save) and get semantic diagnostics(with their cache managed in the object) - * Note that it is only safe to pass BuilderState as old state when creating new state, when - * - If iterator's next method to get next affected file is never called - * - Iteration of single changed file and its dependencies (iteration through all of its affected files) is complete + * Builder to manage the program state changes */ - interface BuilderState { + interface BaseBuilder { /** - * The map of file infos, where there is entry for each file in the program - * The entry is signature of the file (from last emit) or empty string + * Updates the program in the builder to represent new state */ - fileInfos: ReadonlyMap>; + updateProgram(newProgram: Program): void; /** - * Returns true if module gerneration is not ModuleKind.None + * Get all the dependencies of the file */ - isModuleEmit: boolean; + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; + } + /** + * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files + */ + interface SemanticDiagnosticsBuilder extends BaseBuilder { /** - * Map of file referenced or undefined if it wasnt module emit - * The entry is present only if file references other files - * The key is path of file and value is referenced map for that file (for every file referenced, there is entry in the set) + * Gets the semantic diagnostics from the program for the next affected file and caches it + * Returns undefined if the iteration is complete */ - referencedMap: ReadonlyMap | undefined; + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; /** - * Set of source file's paths that have been changed, either in resolution or versions + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics */ - changedFilesSet: ReadonlyMap; - /** - * Set of cached semantic diagnostics per file - */ - semanticDiagnosticsPerFile: ReadonlyMap>; - /** - * Returns true if this state is safe to use as oldState - */ - canCreateNewStateFrom(): boolean; + getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + } + /** + * The builder that can handle the changes in program and iterate through changed file to emit the files + * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files + */ + interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete */ @@ -3759,33 +3767,20 @@ declare namespace ts { /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + * Note that it is assumed that the when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics */ getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; - /** - * Get all the dependencies of the file - */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; } /** - * Information about the source file: Its version and optional signature from last emit + * Create the builder to manage semantic diagnostics and cache them */ - interface FileInfo { - version: string; - signature: string; - } - interface AffectedFileEmitResult extends EmitResult { - affectedFile?: SourceFile; - } + function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder; /** - * Referenced files with values for the keys as referenced file's path to be true + * Create the builder that can handle the changes in program and iterate through changed files + * to emit the those files and manage semantic diagnostics cache as well */ - type ReferencedSet = ReadonlyMap; - interface BuilderOptions { - getCanonicalFileName: (fileName: string) => string; - computeHash: (data: string) => string; - } - function createBuilderState(newProgram: Program, options: BuilderOptions, oldState?: Readonly): BuilderState; + function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; @@ -3819,7 +3814,7 @@ declare namespace ts { declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; /** - * Creates the function that compiles the program by maintaining the builder state and also return diagnostic reporter + * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files */ function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; interface WatchHost { From e102fee363e256cd14e2881f6912e78b4e369333 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 14 Nov 2017 16:53:07 -0800 Subject: [PATCH 008/100] Use the results from affected file enumerator apis as Affected File result --- src/compiler/builder.ts | 35 ++++++++++++------- src/compiler/watch.ts | 8 ++--- .../reference/api/tsserverlibrary.d.ts | 9 ++--- tests/baselines/reference/api/typescript.d.ts | 9 ++--- 4 files changed, 36 insertions(+), 25 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 8703d83aa1d..40aabb04baa 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -331,11 +331,18 @@ namespace ts { } } + /** + * Returns the result with affected file + */ + function toAffectedFileResult(result: T, affectedFile?: SourceFile): AffectedFileResult { + return { result, affectedFile }; + } + /** * Emits the next affected file, and returns the EmitResult along with source files emitted * Returns undefined when iteration is complete */ - function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined { + function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult { const affectedFile = getNextAffectedFile(programOfThisState); if (!affectedFile) { // Done @@ -343,21 +350,22 @@ namespace ts { } else if (affectedFile === programOfThisState) { // When whole program is affected, do emit only once (eg when --out or --outFile is specified) - return programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers); + return toAffectedFileResult(programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers)); } // Emit the affected file const targetSourceFile = affectedFile as SourceFile; - const result = programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers) as AffectedFileEmitResult; - result.affectedFile = targetSourceFile; - return result; + return toAffectedFileResult( + programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), + targetSourceFile + ); } /** * Return the semantic diagnostics for the next affected file or undefined if iteration is complete * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true */ - function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray { + function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult> { while (true) { const affectedFile = getNextAffectedFile(programOfThisState); if (!affectedFile) { @@ -366,7 +374,7 @@ namespace ts { } else if (affectedFile === programOfThisState) { // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) - return programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken); + return toAffectedFileResult(programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken)); } // Get diagnostics for the affected file if its not ignored @@ -376,7 +384,10 @@ namespace ts { continue; } - return getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken); + return toAffectedFileResult( + getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken), + targetSourceFile + ); } } @@ -682,9 +693,7 @@ namespace ts { text: string; } - export interface AffectedFileEmitResult extends EmitResult { - affectedFile?: SourceFile; - } + export type AffectedFileResult = { result: T; affectedFile?: SourceFile; } | undefined; export interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; @@ -714,7 +723,7 @@ namespace ts { * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program @@ -733,7 +742,7 @@ namespace ts { /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index fcb4d98ace0..716846cd606 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -116,11 +116,11 @@ namespace ts { let sourceMaps: SourceMapData[]; let emitSkipped: boolean; - let affectedEmitResult: AffectedFileEmitResult; + let affectedEmitResult: AffectedFileResult; while (affectedEmitResult = builder.emitNextAffectedFile(program, writeFile)) { - emitSkipped = emitSkipped || affectedEmitResult.emitSkipped; - addRange(diagnostics, affectedEmitResult.diagnostics); - sourceMaps = addRange(sourceMaps, affectedEmitResult.sourceMaps); + emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; + addRange(diagnostics, affectedEmitResult.result.diagnostics); + sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); } if (reportSemanticDiagnostics) { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e258d179310..e5ee4d8db6c 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3771,9 +3771,10 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } - interface AffectedFileEmitResult extends EmitResult { + type AffectedFileResult = { + result: T; affectedFile?: SourceFile; - } + } | undefined; interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; computeHash: (data: string) => string; @@ -3799,7 +3800,7 @@ declare namespace ts { * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here @@ -3816,7 +3817,7 @@ declare namespace ts { /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index bac53072302..bd1eff0be6f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3718,9 +3718,10 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } - interface AffectedFileEmitResult extends EmitResult { + type AffectedFileResult = { + result: T; affectedFile?: SourceFile; - } + } | undefined; interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; computeHash: (data: string) => string; @@ -3746,7 +3747,7 @@ declare namespace ts { * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): ReadonlyArray; + getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here @@ -3763,7 +3764,7 @@ declare namespace ts { /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileEmitResult | undefined; + emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here From ffa64e8c4f1ca31263e0e16e299b0542f633fc65 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 16 Nov 2017 11:16:39 -0800 Subject: [PATCH 009/100] Set program as affected if emitting/diagnostics for whole program --- src/compiler/builder.ts | 16 +++++++++++----- .../baselines/reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 40aabb04baa..69f60bb1708 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -334,8 +334,8 @@ namespace ts { /** * Returns the result with affected file */ - function toAffectedFileResult(result: T, affectedFile?: SourceFile): AffectedFileResult { - return { result, affectedFile }; + function toAffectedFileResult(result: T, affected: SourceFile | Program): AffectedFileResult { + return { result, affected }; } /** @@ -350,7 +350,10 @@ namespace ts { } else if (affectedFile === programOfThisState) { // When whole program is affected, do emit only once (eg when --out or --outFile is specified) - return toAffectedFileResult(programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers)); + return toAffectedFileResult( + programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), + programOfThisState + ); } // Emit the affected file @@ -374,7 +377,10 @@ namespace ts { } else if (affectedFile === programOfThisState) { // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) - return toAffectedFileResult(programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken)); + return toAffectedFileResult( + programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), + programOfThisState + ); } // Get diagnostics for the affected file if its not ignored @@ -693,7 +699,7 @@ namespace ts { text: string; } - export type AffectedFileResult = { result: T; affectedFile?: SourceFile; } | undefined; + export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; export interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e5ee4d8db6c..de738e7cbc0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3773,7 +3773,7 @@ declare namespace ts { } type AffectedFileResult = { result: T; - affectedFile?: SourceFile; + affected: SourceFile | Program; } | undefined; interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index bd1eff0be6f..974bc043d3f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3720,7 +3720,7 @@ declare namespace ts { } type AffectedFileResult = { result: T; - affectedFile?: SourceFile; + affected: SourceFile | Program; } | undefined; interface BuilderOptions { getCanonicalFileName: (fileName: string) => string; From 012f12bcbd001165f3f6216105b92eb0aa6f326b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 22 Nov 2017 18:24:53 -0800 Subject: [PATCH 010/100] To handle cancellation token, remove changed/affected files from the changeset only after getting the result --- src/compiler/builder.ts | 100 +++++++++++++++++++++---------- src/compiler/program.ts | 2 +- src/harness/unittests/builder.ts | 72 +++++++++++++++++++++- src/server/project.ts | 2 +- 4 files changed, 141 insertions(+), 35 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 69f60bb1708..ddbb015a2ee 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -22,7 +22,7 @@ namespace ts { * This api is only for internal use */ /*@internal*/ - getFilesAffectedBy(programOfThisState: Program, path: Path): ReadonlyArray; + getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken): ReadonlyArray; } /** @@ -86,7 +86,7 @@ namespace ts { * Get the files affected by the source file. * This is dependent on whether its a module emit or not and hence function expression */ - let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map | undefined) => ReadonlyArray; + let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) => ReadonlyArray; /** * Cache of semantic diagnostics for files with their Path being the key @@ -272,38 +272,65 @@ namespace ts { /** * Gets the files affected by the path from the program */ - function getFilesAffectedBy(programOfThisState: Program, path: Path, cacheToUpdateSignature?: Map): ReadonlyArray { + function getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, cacheToUpdateSignature?: Map): ReadonlyArray { + // Since the operation could be cancelled, the signatures are always stored in the cache + // They will be commited once it is safe to use them + // eg when calling this api from tsserver, if there is no cancellation of the operation + // In the other cases the affected files signatures are commited only after the iteration through the result is complete + const signatureCache = cacheToUpdateSignature || createMap(); const sourceFile = programOfThisState.getSourceFileByPath(path); if (!sourceFile) { return emptyArray; } - if (!updateShapeSignature(programOfThisState, sourceFile, cacheToUpdateSignature)) { + if (!updateShapeSignature(programOfThisState, sourceFile, signatureCache, cancellationToken)) { return [sourceFile]; } - return getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, cacheToUpdateSignature); + const result = getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, signatureCache, cancellationToken); + if (!cacheToUpdateSignature) { + // Commit all the signatures in the signature cache + updateSignaturesFromCache(signatureCache); + } + return result; } - function getNextAffectedFile(programOfThisState: Program): SourceFile | Program | undefined { + /** + * Updates the signatures from the cache + * This should be called whenever it is safe to commit the state of the builder + */ + function updateSignaturesFromCache(signatureCache: Map) { + signatureCache.forEach((signature, path) => { + fileInfos.get(path).signature = signature; + hasCalledUpdateShapeSignature.set(path, true); + }); + } + + /** + * This function returns the next affected file to be processed. + * Note that until doneAffected is called it would keep reporting same result + * This is to allow the callers to be able to actually remove affected file only when the operation is complete + * eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained + */ + function getNextAffectedFile(programOfThisState: Program, cancellationToken: CancellationToken | undefined): SourceFile | Program | undefined { while (true) { if (affectedFiles) { while (affectedFilesIndex < affectedFiles.length) { const affectedFile = affectedFiles[affectedFilesIndex]; - affectedFilesIndex++; if (!seenAffectedFiles.has(affectedFile.path)) { // Set the next affected file as seen and remove the cached semantic diagnostics - seenAffectedFiles.set(affectedFile.path, true); semanticDiagnosticsPerFile.delete(affectedFile.path); return affectedFile; } + seenAffectedFiles.set(affectedFile.path, true); + affectedFilesIndex++; } // Remove the changed file from the change set changedFilesSet.delete(currentChangedFilePath); currentChangedFilePath = undefined; // Commit the changes in file signature - currentAffectedFilesSignatures.forEach((signature, path) => fileInfos.get(path).signature = signature); + updateSignaturesFromCache(currentAffectedFilesSignatures); currentAffectedFilesSignatures.clear(); affectedFiles = undefined; } @@ -320,21 +347,37 @@ namespace ts { // so operations are performed directly on program, return program if (compilerOptions.outFile || compilerOptions.out) { Debug.assert(semanticDiagnosticsPerFile.size === 0); - changedFilesSet.clear(); return programOfThisState; } // Get next batch of affected files + currentAffectedFilesSignatures.clear(); + affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path, cancellationToken, currentAffectedFilesSignatures); currentChangedFilePath = nextKey.value as Path; + semanticDiagnosticsPerFile.delete(currentChangedFilePath); affectedFilesIndex = 0; - affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path, currentAffectedFilesSignatures); + } + } + + /** + * This is called after completing operation on the next affected file. + * The operations here are postponed to ensure that cancellation during the iteration is handled correctly + */ + function doneWithAffectedFile(programOfThisState: Program, affected: SourceFile | Program) { + if (affected === programOfThisState) { + changedFilesSet.clear(); + } + else { + seenAffectedFiles.set((affected).path, true); + affectedFilesIndex++; } } /** * Returns the result with affected file */ - function toAffectedFileResult(result: T, affected: SourceFile | Program): AffectedFileResult { + function toAffectedFileResult(programOfThisState: Program, result: T, affected: SourceFile | Program): AffectedFileResult { + doneWithAffectedFile(programOfThisState, affected); return { result, affected }; } @@ -343,7 +386,7 @@ namespace ts { * Returns undefined when iteration is complete */ function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult { - const affectedFile = getNextAffectedFile(programOfThisState); + const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken); if (!affectedFile) { // Done return undefined; @@ -351,6 +394,7 @@ namespace ts { else if (affectedFile === programOfThisState) { // When whole program is affected, do emit only once (eg when --out or --outFile is specified) return toAffectedFileResult( + programOfThisState, programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), programOfThisState ); @@ -359,6 +403,7 @@ namespace ts { // Emit the affected file const targetSourceFile = affectedFile as SourceFile; return toAffectedFileResult( + programOfThisState, programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), targetSourceFile ); @@ -370,7 +415,7 @@ namespace ts { */ function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult> { while (true) { - const affectedFile = getNextAffectedFile(programOfThisState); + const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken); if (!affectedFile) { // Done return undefined; @@ -378,6 +423,7 @@ namespace ts { else if (affectedFile === programOfThisState) { // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) return toAffectedFileResult( + programOfThisState, programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), programOfThisState ); @@ -387,10 +433,12 @@ namespace ts { const targetSourceFile = affectedFile as SourceFile; if (ignoreSourceFile && ignoreSourceFile(targetSourceFile)) { // Get next affected file + doneWithAffectedFile(programOfThisState, targetSourceFile); continue; } return toAffectedFileResult( + programOfThisState, getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken), targetSourceFile ); @@ -505,16 +553,14 @@ namespace ts { * Returns if the shape of the signature has changed since last emit * Note that it also updates the current signature as the latest signature for the file */ - function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map | undefined) { + function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { Debug.assert(!!sourceFile); // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate - if (hasCalledUpdateShapeSignature.has(sourceFile.path)) { + if (hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { return false; } - Debug.assert(!cacheToUpdateSignature || !cacheToUpdateSignature.has(sourceFile.path)); - hasCalledUpdateShapeSignature.set(sourceFile.path, true); const info = fileInfos.get(sourceFile.path); Debug.assert(!!info); @@ -522,29 +568,19 @@ namespace ts { let latestSignature: string; if (sourceFile.isDeclarationFile) { latestSignature = sourceFile.version; - setLatestSigature(); } else { - const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true); + const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { latestSignature = options.computeHash(emitOutput.outputFiles[0].text); - setLatestSigature(); } else { latestSignature = prevSignature; } } + cacheToUpdateSignature.set(sourceFile.path, latestSignature); return !prevSignature || latestSignature !== prevSignature; - - function setLatestSigature() { - if (cacheToUpdateSignature) { - cacheToUpdateSignature.set(sourceFile.path, latestSignature); - } - else { - info.signature = latestSignature; - } - } } /** @@ -652,7 +688,7 @@ namespace ts { /** * When program emits modular code, gets the files affected by the sourceFile whose shape has changed */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map | undefined) { + function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); } @@ -675,7 +711,7 @@ namespace ts { if (!seenFileNamesMap.has(currentPath)) { const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature)) { + if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken)) { queue.push(...getReferencedByPaths(currentPath)); } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4088a6951a1..213b1fc9601 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1162,7 +1162,7 @@ namespace ts { // This is because in the -out scenario all files need to be emitted, and therefore all // files need to be type checked. And the way to specify that all files need to be type // checked is to not pass the file to getEmitResolver. - const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile); + const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile, cancellationToken); performance.mark("beforeEmit"); diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index a9c16c59fbd..64f43be1376 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -41,9 +41,39 @@ namespace ts { program = updateProgramFile(program, "/b.ts", "namespace B { export const x = 1; }"); assertChanges(["/b.js", "/a.js"]); }); + + it("keeps the file in affected files if cancellation token throws during the operation", () => { + const files: NamedSourceText[] = [ + { name: "/a.ts", text: SourceText.New("", 'import { b } from "./b";', "") }, + { name: "/b.ts", text: SourceText.New("", ' import { c } from "./c";', "export const b = c;") }, + { name: "/c.ts", text: SourceText.New("", "", "export const c = 0;") }, + { name: "/d.ts", text: SourceText.New("", "", "export const dd = 0;") }, + { name: "/e.ts", text: SourceText.New("", "", "export const ee = 0;") }, + ]; + + let program = newProgram(files, ["/d.ts", "/e.ts", "/a.ts"], {}); + const assertChanges = makeAssertChangesWithCancellationToken(() => program); + // No cancellation + assertChanges(["/d.js", "/e.js", "/c.js", "/b.js", "/a.js"]); + + // cancel when emitting a.ts + program = updateProgramFile(program, "/a.ts", "export function foo() { }"); + assertChanges(["/a.js"], 0); + // Change d.ts and verify previously pending a.ts is emitted as well + program = updateProgramFile(program, "/d.ts", "export function bar() { }"); + assertChanges(["/a.js", "/d.js"]); + + // Cancel when emitting b.js + program = updateProgramFile(program, "/b.ts", "export class b { foo() { c + 1; } }"); + program = updateProgramFile(program, "/d.ts", "export function bar2() { }"); + assertChanges(["/d.js", "/b.js", "/a.js"], 1); + // Change e.ts and verify previously b.js as well as a.js get emitted again since previous change was consumed completely but not d.ts + program = updateProgramFile(program, "/e.ts", "export function bar3() { }"); + assertChanges(["/b.js", "/a.js", "/e.js"]); + }); }); - function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { + function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { const builder = createEmitAndSemanticDiagnosticsBuilder({ getCanonicalFileName: identity, computeHash: identity @@ -59,6 +89,46 @@ namespace ts { }; } + function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray, cancelAfterEmitLength?: number) => void { + const builder = createEmitAndSemanticDiagnosticsBuilder({ + getCanonicalFileName: identity, + computeHash: identity + }); + let cancel = false; + const cancellationToken: CancellationToken = { + isCancellationRequested: () => cancel, + throwIfCancellationRequested: () => { + if (cancel) { + throw new OperationCanceledException(); + } + }, + }; + return (fileNames, cancelAfterEmitLength?: number) => { + cancel = false; + let operationWasCancelled = false; + const program = getProgram(); + builder.updateProgram(program); + const outputFileNames: string[] = []; + try { + // tslint:disable-next-line no-empty + do { + assert.isFalse(cancel); + if (outputFileNames.length === cancelAfterEmitLength) { + cancel = true; + } + } while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName), cancellationToken)); + } + catch (e) { + assert.isFalse(operationWasCancelled); + assert.isTrue(e instanceof OperationCanceledException, e.toString()); + operationWasCancelled = true; + } + assert.equal(cancel, operationWasCancelled); + assert.equal(operationWasCancelled, fileNames.length > cancelAfterEmitLength); + assert.deepEqual(outputFileNames, fileNames.slice(0, cancelAfterEmitLength)); + }; + } + function updateProgramFile(program: ProgramWithSourceTexts, fileName: string, fileContent: string): ProgramWithSourceTexts { return updateProgram(program, program.getRootFileNames(), program.getCompilerOptions(), files => { updateProgramText(files, fileName, fileContent); diff --git a/src/server/project.ts b/src/server/project.ts index 3a5785163d2..9b9dbb99436 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -458,7 +458,7 @@ namespace ts.server { }); } this.builder.updateProgram(this.program); - return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path), + return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path, this.cancellationToken), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined); } From 0b79f4a0735c222e21e307ebbcfa6112ff12a89c Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 22 Nov 2017 18:34:49 -0800 Subject: [PATCH 011/100] Handle emit only declaration file to always produce declaration file and skip the diagnostics check --- src/compiler/checker.ts | 6 ++-- src/compiler/declarationEmitter.ts | 2 +- src/compiler/program.ts | 52 ++++++++++++++++-------------- src/compiler/types.ts | 2 +- src/harness/unittests/builder.ts | 7 ++++ 5 files changed, 40 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bc9906f2054..364af70cb55 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -564,10 +564,12 @@ namespace ts { return _jsxNamespace; } - function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken) { + function getEmitResolver(sourceFile: SourceFile, cancellationToken: CancellationToken, ignoreDiagnostics?: boolean) { // Ensure we have all the type information in place for this file so that all the // emitter questions of this resolver will return the right information. - getDiagnostics(sourceFile, cancellationToken); + if (!ignoreDiagnostics) { + getDiagnostics(sourceFile, cancellationToken); + } return emitResolver; } diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index dc76e0ddf50..90d9ff3de5a 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -1994,7 +1994,7 @@ namespace ts { export function writeDeclarationFile(declarationFilePath: string, sourceFileOrBundle: SourceFile | Bundle, host: EmitHost, resolver: EmitResolver, emitterDiagnostics: DiagnosticCollection, emitOnlyDtsFiles: boolean) { const emitDeclarationResult = emitDeclarations(host, resolver, emitterDiagnostics, declarationFilePath, sourceFileOrBundle, emitOnlyDtsFiles); const emitSkipped = emitDeclarationResult.reportedDeclarationError || host.isEmitBlocked(declarationFilePath) || host.getCompilerOptions().noEmit; - if (!emitSkipped) { + if (!emitSkipped || emitOnlyDtsFiles) { const sourceFiles = sourceFileOrBundle.kind === SyntaxKind.Bundle ? sourceFileOrBundle.sourceFiles : [sourceFileOrBundle]; const declarationOutput = emitDeclarationResult.referencesOutput + getDeclarationOutput(emitDeclarationResult.synchronousDeclarationOutput, emitDeclarationResult.moduleElementDeclarationEmitInfo); diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 213b1fc9601..9560859c7ce 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1125,32 +1125,34 @@ namespace ts { function emitWorker(program: Program, sourceFile: SourceFile, writeFileCallback: WriteFileCallback, cancellationToken: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult { let declarationDiagnostics: ReadonlyArray = []; - if (options.noEmit) { - return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true }; - } - - // If the noEmitOnError flag is set, then check if we have any errors so far. If so, - // immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we - // get any preEmit diagnostics, not just the ones - if (options.noEmitOnError) { - const diagnostics = [ - ...program.getOptionsDiagnostics(cancellationToken), - ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), - ...program.getGlobalDiagnostics(cancellationToken), - ...program.getSemanticDiagnostics(sourceFile, cancellationToken) - ]; - - if (diagnostics.length === 0 && program.getCompilerOptions().declaration) { - declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken); + if (!emitOnlyDtsFiles) { + if (options.noEmit) { + return { diagnostics: declarationDiagnostics, sourceMaps: undefined, emittedFiles: undefined, emitSkipped: true }; } - if (diagnostics.length > 0 || declarationDiagnostics.length > 0) { - return { - diagnostics: concatenate(diagnostics, declarationDiagnostics), - sourceMaps: undefined, - emittedFiles: undefined, - emitSkipped: true - }; + // If the noEmitOnError flag is set, then check if we have any errors so far. If so, + // immediately bail out. Note that we pass 'undefined' for 'sourceFile' so that we + // get any preEmit diagnostics, not just the ones + if (options.noEmitOnError) { + const diagnostics = [ + ...program.getOptionsDiagnostics(cancellationToken), + ...program.getSyntacticDiagnostics(sourceFile, cancellationToken), + ...program.getGlobalDiagnostics(cancellationToken), + ...program.getSemanticDiagnostics(sourceFile, cancellationToken) + ]; + + if (diagnostics.length === 0 && program.getCompilerOptions().declaration) { + declarationDiagnostics = program.getDeclarationDiagnostics(/*sourceFile*/ undefined, cancellationToken); + } + + if (diagnostics.length > 0 || declarationDiagnostics.length > 0) { + return { + diagnostics: concatenate(diagnostics, declarationDiagnostics), + sourceMaps: undefined, + emittedFiles: undefined, + emitSkipped: true + }; + } } } @@ -1162,7 +1164,7 @@ namespace ts { // This is because in the -out scenario all files need to be emitted, and therefore all // files need to be type checked. And the way to specify that all files need to be type // checked is to not pass the file to getEmitResolver. - const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile, cancellationToken); + const emitResolver = getDiagnosticsProducingTypeChecker().getEmitResolver((options.outFile || options.out) ? undefined : sourceFile, cancellationToken, emitOnlyDtsFiles); performance.mark("beforeEmit"); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 53c36ac6caa..568a3e2afe4 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2798,7 +2798,7 @@ namespace ts { // Should not be called directly. Should only be accessed through the Program instance. /* @internal */ getDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; /* @internal */ getGlobalDiagnostics(): Diagnostic[]; - /* @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken): EmitResolver; + /* @internal */ getEmitResolver(sourceFile?: SourceFile, cancellationToken?: CancellationToken, ignoreDiagnostics?: boolean): EmitResolver; /* @internal */ getNodeCount(): number; /* @internal */ getIdentifierCount(): number; diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index 64f43be1376..38ac8a4e3d7 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -70,6 +70,13 @@ namespace ts { // Change e.ts and verify previously b.js as well as a.js get emitted again since previous change was consumed completely but not d.ts program = updateProgramFile(program, "/e.ts", "export function bar3() { }"); assertChanges(["/b.js", "/a.js", "/e.js"]); + + // Cancel in the middle of affected files list after b.js emit + program = updateProgramFile(program, "/b.ts", "export class b { foo2() { c + 1; } }"); + assertChanges(["/b.js", "/a.js"], 1); + // Change e.ts and verify previously b.js as well as a.js get emitted again since previous change was consumed completely but not d.ts + program = updateProgramFile(program, "/e.ts", "export function bar5() { }"); + assertChanges(["/b.js", "/a.js", "/e.js"]); }); }); From 3dda2179e8cf7d0b4c6fa474b98c5e10991f5813 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 4 Dec 2017 14:35:37 -0800 Subject: [PATCH 012/100] Rename getProgram to getExistingProgram --- src/compiler/watch.ts | 8 ++++---- src/harness/unittests/reuseProgramStructure.ts | 4 ++-- src/harness/unittests/tscWatchMode.ts | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 5a82584c8c3..70aa8939bcc 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -214,9 +214,9 @@ namespace ts { export interface Watch { /** Synchronize the program with the changes */ synchronizeProgram(): void; - /** Get current program */ + /** Gets the existing program without synchronizing with changes on host */ /*@internal*/ - getProgram(): Program; + getExistingProgram(): Program; } /** @@ -360,8 +360,8 @@ namespace ts { watchConfigFileWildCardDirectories(); return configFileName ? - { getProgram: () => program, synchronizeProgram } : - { getProgram: () => program, synchronizeProgram, updateRootFileNames }; + { getExistingProgram: () => program, synchronizeProgram } : + { getExistingProgram: () => program, synchronizeProgram, updateRootFileNames }; function synchronizeProgram() { writeLog(`Synchronizing program`); diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index cc330300d33..108e2d86694 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -897,12 +897,12 @@ namespace ts { } function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) { - const program = createWatchOfFilesAndCompilerOptions(rootFiles, options, system).getProgram(); + const program = createWatchOfFilesAndCompilerOptions(rootFiles, options, system).getExistingProgram(); verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); } function verifyProgramWithConfigFile(system: System, configFileName: string) { - const program = createWatchOfConfigFile(configFileName, {}, system).getProgram(); + const program = createWatchOfConfigFile(configFileName, {}, system).getExistingProgram(); const { fileNames, options } = parseConfigFile(configFileName, {}, system, notImplemented); verifyProgramIsUptoDate(program, fileNames, options); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 6a83d950ad3..736f8c520b3 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -24,12 +24,12 @@ namespace ts.tscWatch { function createWatchOfConfigFile(configFileName: string, host: WatchedSystem) { const watch = ts.createWatchOfConfigFile(configFileName, {}, host); - return () => watch.getProgram(); + return () => watch.getExistingProgram(); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}) { const watch = ts.createWatchOfFilesAndCompilerOptions(rootFiles, options, host); - return () => watch.getProgram(); + return () => watch.getExistingProgram(); } function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { @@ -237,8 +237,8 @@ namespace ts.tscWatch { const host = createWatchedSystem([configFile, libFile, file1, file2, file3]); const watch = ts.createWatchOfConfigFile(configFile.path, {}, host, notImplemented); - checkProgramActualFiles(watch.getProgram(), [file1.path, libFile.path, file2.path]); - checkProgramRootFiles(watch.getProgram(), [file1.path, file2.path]); + checkProgramActualFiles(watch.getExistingProgram(), [file1.path, libFile.path, file2.path]); + checkProgramRootFiles(watch.getExistingProgram(), [file1.path, file2.path]); checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]); const configDir = getDirectoryPath(configFile.path); checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true); From 61fc9b94de0b7e40030d41d2893e7a61817656e4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 4 Dec 2017 14:40:30 -0800 Subject: [PATCH 013/100] Rename Watch.synchronizeProgram to getProgram and return the updated program as part of this api --- src/compiler/watch.ts | 16 +++++++++------- tests/baselines/reference/api/typescript.d.ts | 4 ++-- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 70aa8939bcc..e00a6751a70 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -212,8 +212,8 @@ namespace ts { } export interface Watch { - /** Synchronize the program with the changes */ - synchronizeProgram(): void; + /** Synchronize with host and get updated program */ + getProgram(): Program; /** Gets the existing program without synchronizing with changes on host */ /*@internal*/ getExistingProgram(): Program; @@ -360,10 +360,10 @@ namespace ts { watchConfigFileWildCardDirectories(); return configFileName ? - { getExistingProgram: () => program, synchronizeProgram } : - { getExistingProgram: () => program, synchronizeProgram, updateRootFileNames }; + { getExistingProgram: () => program, getProgram: synchronizeProgram } : + { getExistingProgram: () => program, getProgram: synchronizeProgram, updateRootFileNames }; - function synchronizeProgram() { + function synchronizeProgram(): Program { writeLog(`Synchronizing program`); if (hasChangedCompilerOptions) { @@ -375,7 +375,7 @@ namespace ts { const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(); if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { - return; + return program; } beforeProgramCreate(compilerOptions); @@ -411,6 +411,7 @@ namespace ts { afterProgramCreate(directoryStructureHost, program); reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + return program; } function updateRootFileNames(files: string[]) { @@ -569,7 +570,8 @@ namespace ts { case ConfigFileProgramReloadLevel.Full: return reloadConfigFile(); default: - return synchronizeProgram(); + synchronizeProgram(); + return; } } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3ea22f19902..df02d53209c 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3850,8 +3850,8 @@ declare namespace ts { onConfigFileDiagnostic(diagnostic: Diagnostic): void; } interface Watch { - /** Synchronize the program with the changes */ - synchronizeProgram(): void; + /** Synchronize with host and get updated program */ + getProgram(): Program; } /** * Creates the watch what generates program using the config file From 471c83b7f59e7df61af49e44811049ebdd90fd00 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 4 Dec 2017 15:08:56 -0800 Subject: [PATCH 014/100] Rename WatchHost.moduleNameResolver to WatchHost.resolveModuleNames to align with compiler host --- src/compiler/resolutionCache.ts | 8 ++++---- src/compiler/watch.ts | 15 ++++++++------- src/server/project.ts | 4 ++-- tests/baselines/reference/api/typescript.d.ts | 2 +- 4 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index e21e81a2e88..1c96dc21f08 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -9,7 +9,7 @@ namespace ts { startRecordingFilesWithChangedResolutions(): void; finishRecordingFilesWithChangedResolutions(): Path[]; - resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, logChanges: boolean): ResolvedModuleFull[]; + resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[]; resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; invalidateResolutionOfFile(filePath: Path): void; @@ -72,7 +72,7 @@ namespace ts { type GetResolutionWithResolvedFileName = (resolution: T) => R; - export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string): ResolutionCache { + export function createResolutionCache(resolutionHost: ResolutionCacheHost, rootDirForResolution: string, logChangesWhenResolvingModule: boolean): ResolutionCache { let filesWithChangedSetOfUnresolvedImports: Path[] | undefined; let filesWithInvalidatedResolutions: Map | undefined; let allFilesHaveInvalidatedResolution = false; @@ -306,12 +306,12 @@ namespace ts { ); } - function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined, logChanges: boolean): ResolvedModuleFull[] { + function resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames: string[] | undefined): ResolvedModuleFull[] { return resolveNamesWithLocalCache( moduleNames, containingFile, resolvedModuleNames, perDirectoryResolvedModuleNames, resolveModuleName, getResolvedModule, - reusedNames, logChanges + reusedNames, logChangesWhenResolvingModule ); } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index e00a6751a70..c9b520c3777 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -171,7 +171,7 @@ namespace ts { afterProgramCreate(host: DirectoryStructureHost, program: Program): void; /** Optional module name resolver */ - moduleNameResolver?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** @@ -310,9 +310,6 @@ namespace ts { const getCachedDirectoryStructureHost = configFileName && (() => directoryStructureHost as CachedDirectoryStructureHost); const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames); let newLine = getNewLineCharacter(compilerOptions, system); - const resolveModuleNames: (moduleNames: string[], containingFile: string, reusedNames?: string[]) => ResolvedModule[] = host.moduleNameResolver ? - (moduleNames, containingFile, reusedNames) => host.moduleNameResolver(moduleNames, containingFile, reusedNames) : - (moduleNames, containingFile, reusedNames?) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false); const compilerHost: CompilerHost & ResolutionCacheHost = { // Members for CompilerHost @@ -332,8 +329,6 @@ namespace ts { getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "", getDirectories: path => directoryStructureHost.getDirectories(path), realpath, - resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile), - resolveModuleNames, onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, @@ -351,8 +346,14 @@ namespace ts { // Cache for the module resolution const resolutionCache = createResolutionCache(compilerHost, configFileName ? getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) : - getCurrentDirectory() + getCurrentDirectory(), + /*logChangesWhenResolvingModule*/ false ); + // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names + compilerHost.resolveModuleNames = host.resolveModuleNames ? + host.resolveModuleNames.bind(host) : + resolutionCache.resolveModuleNames.bind(resolutionCache); + compilerHost.resolveTypeReferenceDirectives = resolutionCache.resolveTypeReferenceDirectives.bind(resolutionCache); synchronizeProgram(); diff --git a/src/server/project.ts b/src/server/project.ts index 9b9dbb99436..800e93f1949 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -237,7 +237,7 @@ namespace ts.server { } // Use the current directory as resolution root only if the project created using current directory string - this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory); + this.resolutionCache = createResolutionCache(this, currentDirectory && this.currentDirectory, /*logChangesWhenResolvingModule*/ true); this.languageService = createLanguageService(this, this.documentRegistry); if (!languageServiceEnabled) { this.disableLanguageService(); @@ -353,7 +353,7 @@ namespace ts.server { } resolveModuleNames(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModuleFull[] { - return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ true); + return this.resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames); } resolveTypeReferenceDirectives(typeDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[] { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index df02d53209c..c82599c18f2 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3828,7 +3828,7 @@ declare namespace ts { /** Custom action after new program creation is successful */ afterProgramCreate(host: DirectoryStructureHost, program: Program): void; /** Optional module name resolver */ - moduleNameResolver?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** * Host to create watch with root files and options From 1a91256c2276972f5393e917b4cf26f7ac855578 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 4 Dec 2017 15:24:22 -0800 Subject: [PATCH 015/100] Make before and after program create callbacks optional --- src/compiler/watch.ts | 14 +++++++------- tests/baselines/reference/api/typescript.d.ts | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index c9b520c3777..5e5a97e16a2 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -165,10 +165,10 @@ namespace ts { /** FS system to use */ system: System; - /** Custom action before creating the program */ - beforeProgramCreate(compilerOptions: CompilerOptions): void; - /** Custom action after new program creation is successful */ - afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + /** If provided, callback to invoke before each program creation */ + beforeProgramCreate?(compilerOptions: CompilerOptions): void; + /** If provided, callback to invoke after every new program creation */ + afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; /** Optional module name resolver */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; @@ -239,7 +239,6 @@ namespace ts { export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { return createWatch({ system, - beforeProgramCreate: noop, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), onConfigFileDiagnostic: reportDiagnostic || createDiagnosticReporter(system), configFileName, @@ -253,7 +252,6 @@ namespace ts { export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { return createWatch({ system, - beforeProgramCreate: noop, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), rootFiles, options @@ -286,7 +284,9 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed - const { system, configFileName, onConfigFileDiagnostic, afterProgramCreate, beforeProgramCreate, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchOfConfigFileHost; + const { system, configFileName, onConfigFileDiagnostic, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchOfConfigFileHost; + const beforeProgramCreate: WatchHost["beforeProgramCreate"] = host.beforeProgramCreate ? host.beforeProgramCreate.bind(host) : noop; + const afterProgramCreate: WatchHost["afterProgramCreate"] = host.afterProgramCreate ? host.afterProgramCreate.bind(host) : noop; let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchOfConfigFileHost; // From tsc we want to get already parsed result and hence check for rootFileNames diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c82599c18f2..13b5b17f50f 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3823,10 +3823,10 @@ declare namespace ts { interface WatchHost { /** FS system to use */ system: System; - /** Custom action before creating the program */ - beforeProgramCreate(compilerOptions: CompilerOptions): void; - /** Custom action after new program creation is successful */ - afterProgramCreate(host: DirectoryStructureHost, program: Program): void; + /** If provided, callback to invoke before each program creation */ + beforeProgramCreate?(compilerOptions: CompilerOptions): void; + /** If provided, callback to invoke after every new program creation */ + afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; /** Optional module name resolver */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } From 944f8b879288581d04e0f69ee649a2361547b700 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 4 Dec 2017 18:17:25 -0800 Subject: [PATCH 016/100] Instead of using system as object on WatchHost, create WatchCompilerHost that combines the functionality --- src/compiler/core.ts | 2 - src/compiler/sys.ts | 4 +- src/compiler/tsc.ts | 41 +++++----- src/compiler/utilities.ts | 4 +- src/compiler/watch.ts | 82 +++++++++++-------- src/server/project.ts | 2 +- src/services/services.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 4 +- tests/baselines/reference/api/typescript.d.ts | 18 ++-- 9 files changed, 87 insertions(+), 72 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 48c1b84dbaa..e1443215c91 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -3023,9 +3023,7 @@ namespace ts { const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); return { useCaseSensitiveFileNames: host.useCaseSensitiveFileNames, - newLine: host.newLine, readFile: (path, encoding) => host.readFile(path, encoding), - write: s => host.write(s), writeFile, fileExists, directoryExists, diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index 841dba8a528..c327bd608de 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -34,9 +34,7 @@ namespace ts { * Partial interface of the System thats needed to support the caching of directory structure */ export interface DirectoryStructureHost { - newLine: string; useCaseSensitiveFileNames: boolean; - write(s: string): void; readFile(path: string, encoding?: string): string | undefined; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; fileExists(path: string): boolean; @@ -49,7 +47,9 @@ namespace ts { } export interface System extends DirectoryStructureHost { + newLine: string; args: string[]; + write(s: string): void; getFileSize?(path: string): number; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 5342319e75d..9a7e98d954c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -150,37 +150,34 @@ namespace ts { return sys.exit(exitStatus); } - function createProgramCompilerWithBuilderState() { - const compilerWithBuilderState = ts.createProgramCompilerWithBuilderState(sys, reportDiagnostic); - return (host: DirectoryStructureHost, program: Program) => { + function createWatchCompilerHost(): WatchCompilerHost { + const watchCompilerHost = ts.createWatchCompilerHost(sys, reportDiagnostic); + const compilerWithBuilderState = watchCompilerHost.afterProgramCreate; + watchCompilerHost.beforeProgramCreate = enableStatistics; + watchCompilerHost.afterProgramCreate = (host, program) => { compilerWithBuilderState(host, program); reportStatistics(program); }; + return watchCompilerHost; } function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { - createWatch({ - system: sys, - beforeProgramCreate: enableStatistics, - afterProgramCreate: createProgramCompilerWithBuilderState(), - onConfigFileDiagnostic: reportDiagnostic, - rootFiles: configParseResult.fileNames, - options: configParseResult.options, - configFileName: configParseResult.options.configFilePath, - optionsToExtend, - configFileSpecs: configParseResult.configFileSpecs, - configFileWildCardDirectories: configParseResult.wildcardDirectories - }); + const watchCompilerHost = createWatchCompilerHost() as WatchCompilerHostOfConfigFile; + watchCompilerHost.onConfigFileDiagnostic = reportDiagnostic; + watchCompilerHost.rootFiles = configParseResult.fileNames; + watchCompilerHost.options = configParseResult.options; + watchCompilerHost.configFileName = configParseResult.options.configFilePath; + watchCompilerHost.optionsToExtend = optionsToExtend; + watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs; + watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories; + createWatch(watchCompilerHost); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { - createWatch({ - system: sys, - beforeProgramCreate: enableStatistics, - afterProgramCreate: createProgramCompilerWithBuilderState(), - rootFiles, - options - }); + const watchCompilerHost = createWatchCompilerHost() as WatchCompilerHostOfFilesAndCompilerOptions; + watchCompilerHost.rootFiles = rootFiles; + watchCompilerHost.options = options; + createWatch(watchCompilerHost); } function compileProgram(program: Program): ExitStatus { diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 0e62c2d8cea..6cfe8c03e9a 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -3306,14 +3306,14 @@ namespace ts { const carriageReturnLineFeed = "\r\n"; const lineFeed = "\n"; - export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, system?: { newLine: string }): string { + export function getNewLineCharacter(options: CompilerOptions | PrinterOptions, getNewLine?: () => string): string { switch (options.newLine) { case NewLineKind.CarriageReturnLineFeed: return carriageReturnLineFeed; case NewLineKind.LineFeed: return lineFeed; } - return system ? system.newLine : sys ? sys.newLine : carriageReturnLineFeed; + return getNewLine ? getNewLine() : sys ? sys.newLine : carriageReturnLineFeed; } /** diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index c2ab3aae591..b5128be552e 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -161,7 +161,7 @@ namespace ts { }; } - export interface WatchHost { + export interface WatchCompilerHost { /** FS system to use */ system: System; @@ -170,14 +170,18 @@ namespace ts { /** If provided, callback to invoke after every new program creation */ afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; - /** Optional module name resolver */ + // Sub set of compiler host methods to read and generate new program + useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + + /** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** * Host to create watch with root files and options */ - export interface WatchOfFilesAndCompilerOptionsHost extends WatchHost { + export interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { /** root files to use to generate program */ rootFiles: string[]; @@ -188,7 +192,7 @@ namespace ts { /** * Host to create watch with config file */ - export interface WatchOfConfigFileHost extends WatchHost { + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { /** Name of the config file to compile */ configFileName: string; @@ -199,11 +203,11 @@ namespace ts { onConfigFileDiagnostic(diagnostic: Diagnostic): void; } - /*@internal*/ /** * Host to create watch with config file that is already parsed (from tsc) */ - export interface WatchOfConfigFileHost extends WatchHost { + /*@internal*/ + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { rootFiles?: string[]; options?: CompilerOptions; optionsToExtend?: CompilerOptions; @@ -233,40 +237,49 @@ namespace ts { updateRootFileNames(fileNames: string[]): void; } + /** + * Creates the watch compiler host that can be extended with config file or root file names and options host + */ + /*@internal*/ + export function createWatchCompilerHost(system = sys, reportDiagnostic?: DiagnosticReporter): WatchCompilerHost { + return { + useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + getNewLine: () => system.newLine, + system, + afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) + }; + } + /** * Create the watched program for config file */ - export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { - return createWatch({ - system, - afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), - onConfigFileDiagnostic: reportDiagnostic || createDiagnosticReporter(system), - configFileName, - optionsToExtend - }); + export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { + const host = createWatchCompilerHost(system) as WatchCompilerHostOfConfigFile; + host.onConfigFileDiagnostic = reportDiagnostic || createDiagnosticReporter(system); + host.configFileName = configFileName; + host.optionsToExtend = optionsToExtend; + return createWatch(host); } /** * Create the watched program for root files and compiler options */ export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { - return createWatch({ - system, - afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic), - rootFiles, - options - }); + const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; + host.rootFiles = rootFiles; + host.options = options; + return createWatch(host); } /** * Creates the watch from the host for root files and compiler options */ - export function createWatch(host: WatchOfFilesAndCompilerOptionsHost): WatchOfFilesAndCompilerOptions; + export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - export function createWatch(host: WatchOfConfigFileHost): WatchOfConfigFile; - export function createWatch(host: WatchOfFilesAndCompilerOptionsHost | WatchOfConfigFileHost): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + export function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { interface HostFileInfo { version: number; sourceFile: SourceFile; @@ -284,10 +297,10 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed - const { system, configFileName, onConfigFileDiagnostic, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchOfConfigFileHost; - const beforeProgramCreate: WatchHost["beforeProgramCreate"] = host.beforeProgramCreate ? host.beforeProgramCreate.bind(host) : noop; - const afterProgramCreate: WatchHost["afterProgramCreate"] = host.afterProgramCreate ? host.afterProgramCreate.bind(host) : noop; - let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchOfConfigFileHost; + const { system, configFileName, onConfigFileDiagnostic, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchCompilerHostOfConfigFile; + const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = host.beforeProgramCreate ? host.beforeProgramCreate.bind(host) : noop; + const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = host.afterProgramCreate ? host.afterProgramCreate.bind(host) : noop; + let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchCompilerHostOfConfigFile; // From tsc we want to get already parsed result and hence check for rootFileNames const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; @@ -296,7 +309,7 @@ namespace ts { } const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; - const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(system.newLine); } : noop; + const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(newLine); } : noop; const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher; const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher; const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher; @@ -308,8 +321,9 @@ namespace ts { const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory()); const realpath = system.realpath && ((path: string) => system.realpath(path)); const getCachedDirectoryStructureHost = configFileName && (() => directoryStructureHost as CachedDirectoryStructureHost); - const getCanonicalFileName = createGetCanonicalFileName(system.useCaseSensitiveFileNames); - let newLine = getNewLineCharacter(compilerOptions, system); + const useCaseSensitiveFileNames = memoize(() => host.useCaseSensitiveFileNames()); + const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames()); + let newLine = updateNewLine(); const compilerHost: CompilerHost & ResolutionCacheHost = { // Members for CompilerHost @@ -319,7 +333,7 @@ namespace ts { getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), writeFile: notImplemented, getCurrentDirectory, - useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + useCaseSensitiveFileNames, getCanonicalFileName, getNewLine: () => newLine, fileExists, @@ -370,7 +384,7 @@ namespace ts { writeLog(`Synchronizing program`); if (hasChangedCompilerOptions) { - newLine = getNewLineCharacter(compilerOptions, system); + newLine = updateNewLine(); if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) { resolutionCache.clear(); } @@ -423,6 +437,10 @@ namespace ts { scheduleProgramUpdate(); } + function updateNewLine() { + return getNewLineCharacter(compilerOptions, () => host.getNewLine()); + } + function toPath(fileName: string) { return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); } diff --git a/src/server/project.ts b/src/server/project.ts index 800e93f1949..40ddaeb2a19 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -266,7 +266,7 @@ namespace ts.server { } getNewLine() { - return this.directoryStructureHost.newLine; + return this.projectService.host.newLine; } getProjectVersion() { diff --git a/src/services/services.ts b/src/services/services.ts index 3c39affcacb..b9a50758326 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1255,7 +1255,7 @@ namespace ts { getCancellationToken: () => cancellationToken, getCanonicalFileName, useCaseSensitiveFileNames: () => useCaseSensitivefileNames, - getNewLine: () => getNewLineCharacter(newSettings, { newLine: getNewLineOrDefaultFromHost(host) }), + getNewLine: () => getNewLineCharacter(newSettings, () => getNewLineOrDefaultFromHost(host)), getDefaultLibFileName: (options) => host.getDefaultLibFileName(options), writeFile: noop, getCurrentDirectory: () => currentDirectory, diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index eb9a788ac7f..f0f9318e687 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2729,9 +2729,7 @@ declare namespace ts { * Partial interface of the System thats needed to support the caching of directory structure */ interface DirectoryStructureHost { - newLine: string; useCaseSensitiveFileNames: boolean; - write(s: string): void; readFile(path: string, encoding?: string): string | undefined; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; fileExists(path: string): boolean; @@ -2743,7 +2741,9 @@ declare namespace ts { exit(exitCode?: number): void; } interface System extends DirectoryStructureHost { + newLine: string; args: string[]; + write(s: string): void; getFileSize?(path: string): number; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 13b5b17f50f..5026237f30e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2729,9 +2729,7 @@ declare namespace ts { * Partial interface of the System thats needed to support the caching of directory structure */ interface DirectoryStructureHost { - newLine: string; useCaseSensitiveFileNames: boolean; - write(s: string): void; readFile(path: string, encoding?: string): string | undefined; writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; fileExists(path: string): boolean; @@ -2743,7 +2741,9 @@ declare namespace ts { exit(exitCode?: number): void; } interface System extends DirectoryStructureHost { + newLine: string; args: string[]; + write(s: string): void; getFileSize?(path: string): number; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that @@ -3820,20 +3820,22 @@ declare namespace ts { * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files */ function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; - interface WatchHost { + interface WatchCompilerHost { /** FS system to use */ system: System; /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; - /** Optional module name resolver */ + useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + /** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** * Host to create watch with root files and options */ - interface WatchOfFilesAndCompilerOptionsHost extends WatchHost { + interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { /** root files to use to generate program */ rootFiles: string[]; /** Compiler options */ @@ -3842,7 +3844,7 @@ declare namespace ts { /** * Host to create watch with config file */ - interface WatchOfConfigFileHost extends WatchHost { + interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { /** Name of the config file to compile */ configFileName: string; /** Options to extend */ @@ -3876,11 +3878,11 @@ declare namespace ts { /** * Creates the watch from the host for root files and compiler options */ - function createWatch(host: WatchOfFilesAndCompilerOptionsHost): WatchOfFilesAndCompilerOptions; + function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - function createWatch(host: WatchOfConfigFileHost): WatchOfConfigFile; + function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; From 43c2610a69748a875b08ca3a3c9e3e9cdece3b83 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 16:34:17 -0800 Subject: [PATCH 017/100] More functions moved from system to WatchCompilerHost --- src/compiler/core.ts | 219 +--------------- src/compiler/program.ts | 1 - src/compiler/resolutionCache.ts | 11 +- src/compiler/sys.ts | 30 +-- src/compiler/tsc.ts | 2 +- src/compiler/types.ts | 1 - src/compiler/watch.ts | 187 ++++++++++---- src/compiler/watchUtilities.ts | 241 ++++++++++++++++++ .../unittests/reuseProgramStructure.ts | 2 +- src/server/editorServices.ts | 2 +- src/server/project.ts | 11 +- src/server/scriptInfo.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 30 +-- tests/baselines/reference/api/typescript.d.ts | 72 ++++-- 14 files changed, 478 insertions(+), 333 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index e1443215c91..b6472987398 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -1398,6 +1398,9 @@ namespace ts { /** Returns its argument. */ export function identity(x: T) { return x; } + /** Returns lower case string */ + export function toLowerCase(x: string) { return x.toLowerCase(); } + /** Throws an error because a function is not implemented. */ export function notImplemented(): never { throw new Error("Not implemented"); @@ -2877,9 +2880,7 @@ namespace ts { export type GetCanonicalFileName = (fileName: string) => string; export function createGetCanonicalFileName(useCaseSensitiveFileNames: boolean): GetCanonicalFileName { - return useCaseSensitiveFileNames - ? ((fileName) => fileName) - : ((fileName) => fileName.toLowerCase()); + return useCaseSensitiveFileNames ? identity : toLowerCase; } /** @@ -3000,215 +3001,7 @@ namespace ts { export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty - export interface FileAndDirectoryExistence { - fileExists: boolean; - directoryExists: boolean; - } - - export interface CachedDirectoryStructureHost extends DirectoryStructureHost { - /** Returns the queried result for the file exists and directory exists if at all it was done */ - addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined; - addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void; - clearCache(): void; - } - - interface MutableFileSystemEntries { - readonly files: string[]; - readonly directories: string[]; - } - - export function createCachedDirectoryStructureHost(host: DirectoryStructureHost): CachedDirectoryStructureHost { - const cachedReadDirectoryResult = createMap(); - const getCurrentDirectory = memoize(() => host.getCurrentDirectory()); - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); - return { - useCaseSensitiveFileNames: host.useCaseSensitiveFileNames, - readFile: (path, encoding) => host.readFile(path, encoding), - writeFile, - fileExists, - directoryExists, - createDirectory, - getCurrentDirectory, - getDirectories, - readDirectory, - addOrDeleteFileOrDirectory, - addOrDeleteFile, - clearCache, - exit: code => host.exit(code) - }; - - function toPath(fileName: string) { - return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); - } - - function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined { - return cachedReadDirectoryResult.get(rootDirPath); - } - - function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined { - return getCachedFileSystemEntries(getDirectoryPath(path)); - } - - function getBaseNameOfFileName(fileName: string) { - return getBaseFileName(normalizePath(fileName)); - } - - function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) { - const resultFromHost: MutableFileSystemEntries = { - files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [], - directories: host.getDirectories(rootDir) || [] - }; - - cachedReadDirectoryResult.set(rootDirPath, resultFromHost); - return resultFromHost; - } - - /** - * If the readDirectory result was already cached, it returns that - * Otherwise gets result from host and caches it. - * The host request is done under try catch block to avoid caching incorrect result - */ - function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined { - const cachedResult = getCachedFileSystemEntries(rootDirPath); - if (cachedResult) { - return cachedResult; - } - - try { - return createCachedFileSystemEntries(rootDir, rootDirPath); - } - catch (_e) { - // If there is exception to read directories, dont cache the result and direct the calls to host - Debug.assert(!cachedReadDirectoryResult.has(rootDirPath)); - return undefined; - } - } - - function fileNameEqual(name1: string, name2: string) { - return getCanonicalFileName(name1) === getCanonicalFileName(name2); - } - - function hasEntry(entries: ReadonlyArray, name: string) { - return some(entries, file => fileNameEqual(file, name)); - } - - function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) { - if (hasEntry(entries, baseName)) { - if (!isValid) { - return filterMutate(entries, entry => !fileNameEqual(entry, baseName)); - } - } - else if (isValid) { - return entries.push(baseName); - } - } - - function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { - const path = toPath(fileName); - const result = getCachedFileSystemEntriesForBaseDir(path); - if (result) { - updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true); - } - return host.writeFile(fileName, data, writeByteOrderMark); - } - - function fileExists(fileName: string): boolean { - const path = toPath(fileName); - const result = getCachedFileSystemEntriesForBaseDir(path); - return result && hasEntry(result.files, getBaseNameOfFileName(fileName)) || - host.fileExists(fileName); - } - - function directoryExists(dirPath: string): boolean { - const path = toPath(dirPath); - return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath); - } - - function createDirectory(dirPath: string) { - const path = toPath(dirPath); - const result = getCachedFileSystemEntriesForBaseDir(path); - const baseFileName = getBaseNameOfFileName(dirPath); - if (result) { - updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true); - } - host.createDirectory(dirPath); - } - - function getDirectories(rootDir: string): string[] { - const rootDirPath = toPath(rootDir); - const result = tryReadDirectory(rootDir, rootDirPath); - if (result) { - return result.directories.slice(); - } - return host.getDirectories(rootDir); - } - - function readDirectory(rootDir: string, extensions?: ReadonlyArray, excludes?: ReadonlyArray, includes?: ReadonlyArray, depth?: number): string[] { - const rootDirPath = toPath(rootDir); - const result = tryReadDirectory(rootDir, rootDirPath); - if (result) { - return matchFiles(rootDir, extensions, excludes, includes, host.useCaseSensitiveFileNames, getCurrentDirectory(), depth, getFileSystemEntries); - } - return host.readDirectory(rootDir, extensions, excludes, includes, depth); - - function getFileSystemEntries(dir: string) { - const path = toPath(dir); - if (path === rootDirPath) { - return result; - } - return getCachedFileSystemEntries(path) || createCachedFileSystemEntries(dir, path); - } - } - - function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) { - const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath); - if (existingResult) { - // Just clear the cache for now - // For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated - clearCache(); - } - else { - // This was earlier a file (hence not in cached directory contents) - // or we never cached the directory containing it - const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrDirectoryPath); - if (parentResult) { - const baseName = getBaseNameOfFileName(fileOrDirectory); - if (parentResult) { - const fsQueryResult: FileAndDirectoryExistence = { - fileExists: host.fileExists(fileOrDirectoryPath), - directoryExists: host.directoryExists(fileOrDirectoryPath) - }; - if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) { - // Folder added or removed, clear the cache instead of updating the folder and its structure - clearCache(); - } - else { - // No need to update the directory structure, just files - updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists); - } - return fsQueryResult; - } - } - } - } - - function addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind) { - if (eventKind === FileWatcherEventKind.Changed) { - return; - } - - const parentResult = getCachedFileSystemEntriesForBaseDir(filePath); - if (parentResult) { - updateFilesOfFileSystemEntry(parentResult, getBaseNameOfFileName(fileName), eventKind === FileWatcherEventKind.Created); - } - } - - function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) { - updateFileSystemEntry(parentResult.files, baseName, fileExists); - } - - function clearCache() { - cachedReadDirectoryResult.clear(); - } + export function getBoundFunction(method: T | undefined, methodOf: {}): T | undefined { + return method && method.bind(methodOf); } } diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9560859c7ce..9960b501d78 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -191,7 +191,6 @@ namespace ts { readFile: fileName => sys.readFile(fileName), trace: (s: string) => sys.write(s + newLine), directoryExists: directoryName => sys.directoryExists(directoryName), - getEnvironmentVariable: name => sys.getEnvironmentVariable ? sys.getEnvironmentVariable(name) : "", getDirectories: (path: string) => sys.getDirectories(path), realpath }; diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 1c96dc21f08..92eea4140da 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -47,7 +47,7 @@ namespace ts { onInvalidatedResolution(): void; watchTypeRootsDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher; onChangedAutomaticTypeDirectiveNames(): void; - getCachedDirectoryStructureHost?(): CachedDirectoryStructureHost; + getCachedDirectoryStructureHost(): CachedDirectoryStructureHost | undefined; projectName?: string; getGlobalCache?(): string | undefined; writeLog(s: string): void; @@ -87,6 +87,7 @@ namespace ts { const perDirectoryResolvedTypeReferenceDirectives = createMap>(); const getCurrentDirectory = memoize(() => resolutionHost.getCurrentDirectory()); + const cachedDirectoryStructureHost = resolutionHost.getCachedDirectoryStructureHost(); /** * These are the extensions that failed lookup files will have by default, @@ -467,9 +468,9 @@ namespace ts { function createDirectoryWatcher(directory: string, dirPath: Path) { return resolutionHost.watchDirectoryOfFailedLookupLocation(directory, fileOrDirectory => { const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory); - if (resolutionHost.getCachedDirectoryStructureHost) { + if (cachedDirectoryStructureHost) { // Since the file existance changed, update the sourceFiles cache - resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); + cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } // If the files are added to project root or node_modules directory, always run through the invalidation process @@ -596,9 +597,9 @@ namespace ts { // Create new watch and recursive info return resolutionHost.watchTypeRootsDirectory(typeRoot, fileOrDirectory => { const fileOrDirectoryPath = resolutionHost.toPath(fileOrDirectory); - if (resolutionHost.getCachedDirectoryStructureHost) { + if (cachedDirectoryStructureHost) { // Since the file existance changed, update the sourceFiles cache - resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); + cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } // For now just recompile diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index c327bd608de..f744b9ab0eb 100644 --- a/src/compiler/sys.ts +++ b/src/compiler/sys.ts @@ -30,27 +30,14 @@ namespace ts { mtime?: Date; } - /** - * Partial interface of the System thats needed to support the caching of directory structure - */ - export interface DirectoryStructureHost { - useCaseSensitiveFileNames: boolean; - readFile(path: string, encoding?: string): string | undefined; - writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; - exit(exitCode?: number): void; - } - - export interface System extends DirectoryStructureHost { - newLine: string; + export interface System { args: string[]; + newLine: string; + useCaseSensitiveFileNames: boolean; write(s: string): void; + readFile(path: string, encoding?: string): string | undefined; getFileSize?(path: string): number; + writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -58,7 +45,13 @@ namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; getExecutingFilePath(): string; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -66,6 +59,7 @@ namespace ts { */ createHash?(data: string): string; getMemoryUsage?(): number; + exit(exitCode?: number): void; realpath?(path: string): string; /*@internal*/ getEnvironmentVariable(name: string): string; /*@internal*/ tryEnableSourceMapsForHost?(): void; diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 9a7e98d954c..daccfca33b8 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -110,7 +110,7 @@ namespace ts { const commandLineOptions = commandLine.options; if (configFileName) { - const configParseResult = parseConfigFile(configFileName, commandLineOptions, sys, reportDiagnostic); + const configParseResult = parseConfigFileWithSystem(configFileName, commandLineOptions, sys, reportDiagnostic); udpateReportDiagnostic(configParseResult.options); if (isWatchSet(configParseResult.options)) { reportWatchModeWithoutSysSupport(); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0167bd50116..4b3c15df706 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4327,7 +4327,6 @@ namespace ts { * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; - getEnvironmentVariable?(name: string): string; /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index b5128be552e..0bd0fc499b7 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -33,34 +33,52 @@ namespace ts { }; } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + /*@internal*/ + export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; + } + + /** Parses config file using System interface */ + /*@internal*/ + export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) { + const host: ParseConfigFileHost = system; + host.onConfigFileDiagnostic = reportDiagnostic; + host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic); + const result = parseConfigFile(configFileName, optionsToExtend, host); + host.onConfigFileDiagnostic = undefined; + host.onUnRecoverableConfigFileDiagnostic = undefined; + return result; + } + /** * Reads the config file, reports errors if any and exits if the config file cannot be found */ /*@internal*/ - export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, system: DirectoryStructureHost, reportDiagnostic: DiagnosticReporter): ParsedCommandLine { + export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { let configFileText: string; try { - configFileText = system.readFile(configFileName); + configFileText = host.readFile(configFileName); } catch (e) { const error = createCompilerDiagnostic(Diagnostics.Cannot_read_file_0_Colon_1, configFileName, e.message); - reportDiagnostic(error); - system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; } if (!configFileText) { const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - reportDiagnostic(error); - system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; } const result = parseJsonText(configFileName, configFileText); - result.parseDiagnostics.forEach(reportDiagnostic); + result.parseDiagnostics.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic)); - const cwd = system.getCurrentDirectory(); - const configParseResult = parseJsonSourceFileConfigFileContent(result, system, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); - configParseResult.errors.forEach(reportDiagnostic); + const cwd = host.getCurrentDirectory(); + const configParseResult = parseJsonSourceFileConfigFileContent(result, host, getNormalizedAbsolutePath(getDirectoryPath(configFileName), cwd), optionsToExtend, getNormalizedAbsolutePath(configFileName, cwd)); + configParseResult.errors.forEach(diagnostic => host.onConfigFileDiagnostic(diagnostic)); return configParseResult; } @@ -90,7 +108,7 @@ namespace ts { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); const builder = createEmitAndSemanticDiagnosticsBuilder({ getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), - computeHash: system.createHash ? system.createHash.bind(system) : identity + computeHash: getBoundFunction(system.createHash, system) || identity }); return (host: DirectoryStructureHost, program: Program) => { @@ -173,8 +191,30 @@ namespace ts { // Sub set of compiler host methods to read and generate new program useCaseSensitiveFileNames(): boolean; getNewLine(): string; + getCurrentDirectory(): string; - /** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */ + /** + * Use to check file presence for source files and + * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well + */ + fileExists(path: string): boolean; + /** + * Use to read file text for source files and + * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well + */ + readFile(path: string, encoding?: string): string | undefined; + + /** If provided, used for module resolution as well as to handle directory structure */ + directoryExists?(path: string): boolean; + /** If provided, used in resolutions as well as handling directory structure */ + getDirectories?(path: string): string[]; + /** If provided, used to cache and handle directory structure modifications */ + readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + + /** Symbol links resolution */ + realpath?(path: string): string; + + /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } @@ -189,18 +229,36 @@ namespace ts { options: CompilerOptions; } + /** + * Reports config file diagnostics + */ + export interface ConfigFileDiagnosticsReporter { + /** + * Reports the diagnostics in reading/writing or parsing of the config file + */ + onConfigFileDiagnostic(diagnostic: Diagnostic): void; + + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + } + /** * Host to create watch with config file */ - export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { /** Name of the config file to compile */ configFileName: string; /** Options to extend */ optionsToExtend?: CompilerOptions; - // Reports errors in the config file - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + /** + * Used to generate source file names from the config file and its include, exclude, files rules + * and also to cache the directory stucture + */ + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; } /** @@ -241,21 +299,39 @@ namespace ts { * Creates the watch compiler host that can be extended with config file or root file names and options host */ /*@internal*/ - export function createWatchCompilerHost(system = sys, reportDiagnostic?: DiagnosticReporter): WatchCompilerHost { + export function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { return { useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, + getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), + fileExists: getBoundFunction(system.fileExists, system), + readFile: getBoundFunction(system.readFile, system), + directoryExists: getBoundFunction(system.directoryExists, system), + getDirectories: getBoundFunction(system.getDirectories, system), + readDirectory: getBoundFunction(system.readDirectory, system), + realpath: getBoundFunction(system.realpath, system), system, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) }; } + /** + * Report error and exit + */ + /*@internal*/ + export function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { + reportDiagnostic(diagnostic); + system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + /** * Create the watched program for config file */ export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { - const host = createWatchCompilerHost(system) as WatchCompilerHostOfConfigFile; - host.onConfigFileDiagnostic = reportDiagnostic || createDiagnosticReporter(system); + reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); + const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile; + host.onConfigFileDiagnostic = reportDiagnostic; + host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); host.configFileName = configFileName; host.optionsToExtend = optionsToExtend; return createWatch(host); @@ -279,7 +355,7 @@ namespace ts { * Creates the watch from the host for config file */ export function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; - export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { interface HostFileInfo { version: number; sourceFile: SourceFile; @@ -297,13 +373,30 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed - const { system, configFileName, onConfigFileDiagnostic, optionsToExtend: optionsToExtendForConfigFile = {} } = host as WatchCompilerHostOfConfigFile; - const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = host.beforeProgramCreate ? host.beforeProgramCreate.bind(host) : noop; - const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = host.afterProgramCreate ? host.afterProgramCreate.bind(host) : noop; - let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host as WatchCompilerHostOfConfigFile; + const system = host.system; + const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); + const currentDirectory = host.getCurrentDirectory(); + const getCurrentDirectory = () => currentDirectory; + const onConfigFileDiagnostic = getBoundFunction(host.onConfigFileDiagnostic, host); + const readFile = getBoundFunction(host.readFile, host); + const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host; + const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = getBoundFunction(host.beforeProgramCreate, host) || noop; + const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = getBoundFunction(host.afterProgramCreate, host) || noop; + let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host; + + const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); + const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host; + const parseConfigFileHost: ParseConfigFileHost = { + useCaseSensitiveFileNames, + readDirectory: getBoundFunction(directoryStructureHost.readDirectory, directoryStructureHost), + fileExists: getBoundFunction(directoryStructureHost.fileExists, directoryStructureHost), + readFile, + getCurrentDirectory, + onConfigFileDiagnostic, + onUnRecoverableConfigFileDiagnostic: getBoundFunction(host.onUnRecoverableConfigFileDiagnostic, host) + }; // From tsc we want to get already parsed result and hence check for rootFileNames - const directoryStructureHost = configFileName ? createCachedDirectoryStructureHost(system) : system; if (configFileName && !rootFileNames) { parseConfigFile(); } @@ -318,11 +411,7 @@ namespace ts { watchFile(system, configFileName, scheduleProgramReload, writeLog); } - const getCurrentDirectory = memoize(() => directoryStructureHost.getCurrentDirectory()); - const realpath = system.realpath && ((path: string) => system.realpath(path)); - const getCachedDirectoryStructureHost = configFileName && (() => directoryStructureHost as CachedDirectoryStructureHost); - const useCaseSensitiveFileNames = memoize(() => host.useCaseSensitiveFileNames()); - const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames()); + const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); let newLine = updateNewLine(); const compilerHost: CompilerHost & ResolutionCacheHost = { @@ -333,23 +422,22 @@ namespace ts { getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), writeFile: notImplemented, getCurrentDirectory, - useCaseSensitiveFileNames, + useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, getCanonicalFileName, getNewLine: () => newLine, fileExists, - readFile: fileName => system.readFile(fileName), + readFile, trace: s => system.write(s + newLine), - directoryExists: directoryName => directoryStructureHost.directoryExists(directoryName), - getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "", - getDirectories: path => directoryStructureHost.getDirectories(path), - realpath, + directoryExists: getBoundFunction(directoryStructureHost.directoryExists, directoryStructureHost), + getDirectories: getBoundFunction(directoryStructureHost.getDirectories, directoryStructureHost), + realpath: getBoundFunction(host.realpath, host), onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, getCompilationSettings: () => compilerOptions, watchDirectoryOfFailedLookupLocation: watchDirectory, watchTypeRootsDirectory: watchDirectory, - getCachedDirectoryStructureHost, + getCachedDirectoryStructureHost: () => cachedDirectoryStructureHost, onInvalidatedResolution: scheduleProgramUpdate, onChangedAutomaticTypeDirectiveNames: () => { hasChangedAutomaticTypeDirectiveNames = true; @@ -359,8 +447,8 @@ namespace ts { }; // Cache for the module resolution const resolutionCache = createResolutionCache(compilerHost, configFileName ? - getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) : - getCurrentDirectory(), + getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) : + currentDirectory, /*logChangesWhenResolvingModule*/ false ); // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names @@ -442,7 +530,7 @@ namespace ts { } function toPath(fileName: string) { - return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); + return ts.toPath(fileName, currentDirectory, getCanonicalFileName); } function fileExists(fileName: string) { @@ -505,7 +593,7 @@ namespace ts { let text: string; try { performance.mark("beforeIORead"); - text = system.readFile(fileName, compilerOptions.charset); + text = host.readFile(fileName, compilerOptions.charset); performance.mark("afterIORead"); performance.measure("I/O Read", "beforeIORead", "afterIORead"); } @@ -601,7 +689,7 @@ namespace ts { } function reloadFileNamesFromConfigFile() { - const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, directoryStructureHost); + const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost); if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) { onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); } @@ -615,8 +703,9 @@ namespace ts { writeLog(`Reloading config file: ${configFileName}`); reloadLevel = ConfigFileProgramReloadLevel.None; - const cachedHost = directoryStructureHost as CachedDirectoryStructureHost; - cachedHost.clearCache(); + if (cachedDirectoryStructureHost) { + cachedDirectoryStructureHost.clearCache(); + } parseConfigFile(); hasChangedCompilerOptions = true; synchronizeProgram(); @@ -626,7 +715,7 @@ namespace ts { } function parseConfigFile() { - const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, directoryStructureHost as CachedDirectoryStructureHost, onConfigFileDiagnostic); + const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost); rootFileNames = configParseResult.fileNames; compilerOptions = configParseResult.options; configFileSpecs = configParseResult.configFileSpecs; @@ -662,8 +751,8 @@ namespace ts { } function updateCachedSystemWithFile(fileName: string, path: Path, eventKind: FileWatcherEventKind) { - if (configFileName) { - (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(fileName, path, eventKind); + if (cachedDirectoryStructureHost) { + cachedDirectoryStructureHost.addOrDeleteFile(fileName, path, eventKind); } } @@ -712,7 +801,7 @@ namespace ts { const fileOrDirectoryPath = toPath(fileOrDirectory); // Since the file existance changed, update the sourceFiles cache - const result = (directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); + const result = cachedDirectoryStructureHost && cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); // Instead of deleting the file, mark it as changed instead // Many times node calls add/remove/file when watching directories recursively diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 0cf38f372d9..a1d22c20ac1 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -2,6 +2,247 @@ /* @internal */ namespace ts { + /** + * Partial interface of the System thats needed to support the caching of directory structure + */ + export interface DirectoryStructureHost { + fileExists(path: string): boolean; + readFile(path: string, encoding?: string): string | undefined; + + directoryExists?(path: string): boolean; + getDirectories?(path: string): string[]; + readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + + createDirectory?(path: string): void; + writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + } + + interface FileAndDirectoryExistence { + fileExists: boolean; + directoryExists: boolean; + } + + export interface CachedDirectoryStructureHost extends DirectoryStructureHost { + useCaseSensitiveFileNames: boolean; + + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + + /** Returns the queried result for the file exists and directory exists if at all it was done */ + addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path): FileAndDirectoryExistence | undefined; + addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind): void; + clearCache(): void; + } + + interface MutableFileSystemEntries { + readonly files: string[]; + readonly directories: string[]; + } + + export function createCachedDirectoryStructureHost(host: DirectoryStructureHost, currentDirectory: string, useCaseSensitiveFileNames: boolean): CachedDirectoryStructureHost | undefined { + if (!host.getDirectories || !host.readDirectory) { + return undefined; + } + + const cachedReadDirectoryResult = createMap(); + const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + return { + useCaseSensitiveFileNames, + fileExists, + readFile: getBoundFunction(host.readFile, host), + directoryExists: host.directoryExists && directoryExists, + getDirectories, + readDirectory, + createDirectory, + writeFile, + addOrDeleteFileOrDirectory, + addOrDeleteFile, + clearCache + }; + + function toPath(fileName: string) { + return ts.toPath(fileName, currentDirectory, getCanonicalFileName); + } + + function getCachedFileSystemEntries(rootDirPath: Path): MutableFileSystemEntries | undefined { + return cachedReadDirectoryResult.get(rootDirPath); + } + + function getCachedFileSystemEntriesForBaseDir(path: Path): MutableFileSystemEntries | undefined { + return getCachedFileSystemEntries(getDirectoryPath(path)); + } + + function getBaseNameOfFileName(fileName: string) { + return getBaseFileName(normalizePath(fileName)); + } + + function createCachedFileSystemEntries(rootDir: string, rootDirPath: Path) { + const resultFromHost: MutableFileSystemEntries = { + files: map(host.readDirectory(rootDir, /*extensions*/ undefined, /*exclude*/ undefined, /*include*/["*.*"]), getBaseNameOfFileName) || [], + directories: host.getDirectories(rootDir) || [] + }; + + cachedReadDirectoryResult.set(rootDirPath, resultFromHost); + return resultFromHost; + } + + /** + * If the readDirectory result was already cached, it returns that + * Otherwise gets result from host and caches it. + * The host request is done under try catch block to avoid caching incorrect result + */ + function tryReadDirectory(rootDir: string, rootDirPath: Path): MutableFileSystemEntries | undefined { + const cachedResult = getCachedFileSystemEntries(rootDirPath); + if (cachedResult) { + return cachedResult; + } + + try { + return createCachedFileSystemEntries(rootDir, rootDirPath); + } + catch (_e) { + // If there is exception to read directories, dont cache the result and direct the calls to host + Debug.assert(!cachedReadDirectoryResult.has(rootDirPath)); + return undefined; + } + } + + function fileNameEqual(name1: string, name2: string) { + return getCanonicalFileName(name1) === getCanonicalFileName(name2); + } + + function hasEntry(entries: ReadonlyArray, name: string) { + return some(entries, file => fileNameEqual(file, name)); + } + + function updateFileSystemEntry(entries: string[], baseName: string, isValid: boolean) { + if (hasEntry(entries, baseName)) { + if (!isValid) { + return filterMutate(entries, entry => !fileNameEqual(entry, baseName)); + } + } + else if (isValid) { + return entries.push(baseName); + } + } + + function writeFile(fileName: string, data: string, writeByteOrderMark?: boolean): void { + const path = toPath(fileName); + const result = getCachedFileSystemEntriesForBaseDir(path); + if (result) { + updateFilesOfFileSystemEntry(result, getBaseNameOfFileName(fileName), /*fileExists*/ true); + } + return host.writeFile(fileName, data, writeByteOrderMark); + } + + function fileExists(fileName: string): boolean { + const path = toPath(fileName); + const result = getCachedFileSystemEntriesForBaseDir(path); + return result && hasEntry(result.files, getBaseNameOfFileName(fileName)) || + host.fileExists(fileName); + } + + function directoryExists(dirPath: string): boolean { + const path = toPath(dirPath); + return cachedReadDirectoryResult.has(path) || host.directoryExists(dirPath); + } + + function createDirectory(dirPath: string) { + const path = toPath(dirPath); + const result = getCachedFileSystemEntriesForBaseDir(path); + const baseFileName = getBaseNameOfFileName(dirPath); + if (result) { + updateFileSystemEntry(result.directories, baseFileName, /*isValid*/ true); + } + host.createDirectory(dirPath); + } + + function getDirectories(rootDir: string): string[] { + const rootDirPath = toPath(rootDir); + const result = tryReadDirectory(rootDir, rootDirPath); + if (result) { + return result.directories.slice(); + } + return host.getDirectories(rootDir); + } + + function readDirectory(rootDir: string, extensions?: ReadonlyArray, excludes?: ReadonlyArray, includes?: ReadonlyArray, depth?: number): string[] { + const rootDirPath = toPath(rootDir); + const result = tryReadDirectory(rootDir, rootDirPath); + if (result) { + return matchFiles(rootDir, extensions, excludes, includes, useCaseSensitiveFileNames, currentDirectory, depth, getFileSystemEntries); + } + return host.readDirectory(rootDir, extensions, excludes, includes, depth); + + function getFileSystemEntries(dir: string) { + const path = toPath(dir); + if (path === rootDirPath) { + return result; + } + return getCachedFileSystemEntries(path) || createCachedFileSystemEntries(dir, path); + } + } + + function addOrDeleteFileOrDirectory(fileOrDirectory: string, fileOrDirectoryPath: Path) { + const existingResult = getCachedFileSystemEntries(fileOrDirectoryPath); + if (existingResult) { + // Just clear the cache for now + // For now just clear the cache, since this could mean that multiple level entries might need to be re-evaluated + clearCache(); + return undefined; + } + + const parentResult = getCachedFileSystemEntriesForBaseDir(fileOrDirectoryPath); + if (!parentResult) { + return undefined; + } + + // This was earlier a file (hence not in cached directory contents) + // or we never cached the directory containing it + + if (!host.directoryExists) { + // Since host doesnt support directory exists, clear the cache as otherwise it might not be same + clearCache(); + return undefined; + } + + const baseName = getBaseNameOfFileName(fileOrDirectory); + const fsQueryResult: FileAndDirectoryExistence = { + fileExists: host.fileExists(fileOrDirectoryPath), + directoryExists: host.directoryExists(fileOrDirectoryPath) + }; + if (fsQueryResult.directoryExists || hasEntry(parentResult.directories, baseName)) { + // Folder added or removed, clear the cache instead of updating the folder and its structure + clearCache(); + } + else { + // No need to update the directory structure, just files + updateFilesOfFileSystemEntry(parentResult, baseName, fsQueryResult.fileExists); + } + return fsQueryResult; + + } + + function addOrDeleteFile(fileName: string, filePath: Path, eventKind: FileWatcherEventKind) { + if (eventKind === FileWatcherEventKind.Changed) { + return; + } + + const parentResult = getCachedFileSystemEntriesForBaseDir(filePath); + if (parentResult) { + updateFilesOfFileSystemEntry(parentResult, getBaseNameOfFileName(fileName), eventKind === FileWatcherEventKind.Created); + } + } + + function updateFilesOfFileSystemEntry(parentResult: MutableFileSystemEntries, baseName: string, fileExists: boolean) { + updateFileSystemEntry(parentResult.files, baseName, fileExists); + } + + function clearCache() { + cachedReadDirectoryResult.clear(); + } + } + export enum ConfigFileProgramReloadLevel { None, /** Update the file name list from the disk */ diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 92e40cc8f39..5ff1f08332c 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -903,7 +903,7 @@ namespace ts { function verifyProgramWithConfigFile(system: System, configFileName: string) { const program = createWatchOfConfigFile(configFileName, {}, system).getExistingProgram(); - const { fileNames, options } = parseConfigFile(configFileName, {}, system, notImplemented); + const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented); verifyProgramIsUptoDate(program, fileNames, options); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a5b09c6a63f..bafe90a59cb 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1497,7 +1497,7 @@ namespace ts.server { } private createConfiguredProject(configFileName: NormalizedPath) { - const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host); + const cachedDirectoryStructureHost = createCachedDirectoryStructureHost(this.host, this.host.getCurrentDirectory(), this.host.useCaseSensitiveFileNames); const { projectOptions, configFileErrors, configFileSpecs } = this.convertConfigFileContentToProjectOptions(configFileName, cachedDirectoryStructureHost); this.logger.info(`Opened configuration file ${configFileName}`); const languageServiceEnabled = !this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader); diff --git a/src/server/project.ts b/src/server/project.ts index 40ddaeb2a19..9ae1b1d0b6e 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -334,7 +334,7 @@ namespace ts.server { } useCaseSensitiveFileNames() { - return this.directoryStructureHost.useCaseSensitiveFileNames; + return this.projectService.host.useCaseSensitiveFileNames; } readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[] { @@ -342,7 +342,7 @@ namespace ts.server { } readFile(fileName: string): string | undefined { - return this.directoryStructureHost.readFile(fileName); + return this.projectService.host.readFile(fileName); } fileExists(file: string): boolean { @@ -368,6 +368,11 @@ namespace ts.server { return this.directoryStructureHost.getDirectories(path); } + /*@internal*/ + getCachedDirectoryStructureHost(): CachedDirectoryStructureHost { + return undefined; + } + /*@internal*/ toPath(fileName: string) { return toPath(fileName, this.currentDirectory, this.projectService.toCanonicalFileName); @@ -877,7 +882,7 @@ namespace ts.server { missingFilePath, (fileName, eventKind) => { if (this.projectKind === ProjectKind.Configured) { - (this.directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(fileName, missingFilePath, eventKind); + this.getCachedDirectoryStructureHost().addOrDeleteFile(fileName, missingFilePath, eventKind); } if (eventKind === FileWatcherEventKind.Created && this.missingFilesMap.has(missingFilePath)) { diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index f800a1117d0..329d403195d 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -313,7 +313,7 @@ namespace ts.server { detachAllProjects() { for (const p of this.containingProjects) { if (p.projectKind === ProjectKind.Configured) { - (p.directoryStructureHost as CachedDirectoryStructureHost).addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted); + p.getCachedDirectoryStructureHost().addOrDeleteFile(this.fileName, this.path, FileWatcherEventKind.Deleted); } const isInfoRoot = p.isRoot(this); // detach is unnecessary since we'll clean the list of containing projects anyways diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index f0f9318e687..64f595664b3 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2477,7 +2477,6 @@ declare namespace ts { * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; - getEnvironmentVariable?(name: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; @@ -2725,26 +2724,14 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - /** - * Partial interface of the System thats needed to support the caching of directory structure - */ - interface DirectoryStructureHost { - useCaseSensitiveFileNames: boolean; - readFile(path: string, encoding?: string): string | undefined; - writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; - exit(exitCode?: number): void; - } - interface System extends DirectoryStructureHost { - newLine: string; + interface System { args: string[]; + newLine: string; + useCaseSensitiveFileNames: boolean; write(s: string): void; + readFile(path: string, encoding?: string): string | undefined; getFileSize?(path: string): number; + writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -2752,7 +2739,13 @@ declare namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; getExecutingFilePath(): string; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -2760,6 +2753,7 @@ declare namespace ts { */ createHash?(data: string): string; getMemoryUsage?(): number; + exit(exitCode?: number): void; realpath?(path: string): string; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 5026237f30e..c74fc46d60e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2477,7 +2477,6 @@ declare namespace ts { * This method is a companion for 'resolveModuleNames' and is used to resolve 'types' references to actual type declaration files */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; - getEnvironmentVariable?(name: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; @@ -2725,26 +2724,14 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - /** - * Partial interface of the System thats needed to support the caching of directory structure - */ - interface DirectoryStructureHost { - useCaseSensitiveFileNames: boolean; - readFile(path: string, encoding?: string): string | undefined; - writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; - fileExists(path: string): boolean; - directoryExists(path: string): boolean; - createDirectory(path: string): void; - getCurrentDirectory(): string; - getDirectories(path: string): string[]; - readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; - exit(exitCode?: number): void; - } - interface System extends DirectoryStructureHost { - newLine: string; + interface System { args: string[]; + newLine: string; + useCaseSensitiveFileNames: boolean; write(s: string): void; + readFile(path: string, encoding?: string): string | undefined; getFileSize?(path: string): number; + writeFile(path: string, data: string, writeByteOrderMark?: boolean): void; /** * @pollingInterval - this parameter is used in polling-based watchers and ignored in watchers that * use native OS file watching @@ -2752,7 +2739,13 @@ declare namespace ts { watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; watchDirectory?(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; resolvePath(path: string): string; + fileExists(path: string): boolean; + directoryExists(path: string): boolean; + createDirectory(path: string): void; getExecutingFilePath(): string; + getCurrentDirectory(): string; + getDirectories(path: string): string[]; + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; getModifiedTime?(path: string): Date; /** * This should be cryptographically secure. @@ -2760,6 +2753,7 @@ declare namespace ts { */ createHash?(data: string): string; getMemoryUsage?(): number; + exit(exitCode?: number): void; realpath?(path: string): string; setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout?(timeoutId: any): void; @@ -3829,7 +3823,26 @@ declare namespace ts { afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; useCaseSensitiveFileNames(): boolean; getNewLine(): string; - /** If provided this function would be used to resolve the module names, otherwise typescript's default module resolution */ + getCurrentDirectory(): string; + /** + * Use to check file presence for source files and + * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well + */ + fileExists(path: string): boolean; + /** + * Use to read file text for source files and + * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well + */ + readFile(path: string, encoding?: string): string | undefined; + /** If provided, used for module resolution as well as to handle directory structure */ + directoryExists?(path: string): boolean; + /** If provided, used in resolutions as well as handling directory structure */ + getDirectories?(path: string): string[]; + /** If provided, used to cache and handle directory structure modifications */ + readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; + /** Symbol links resolution */ + realpath?(path: string): string; + /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; } /** @@ -3841,15 +3854,32 @@ declare namespace ts { /** Compiler options */ options: CompilerOptions; } + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports the diagnostics in reading/writing or parsing of the config file + */ + onConfigFileDiagnostic(diagnostic: Diagnostic): void; + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + } /** * Host to create watch with config file */ - interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { + interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { /** Name of the config file to compile */ configFileName: string; /** Options to extend */ optionsToExtend?: CompilerOptions; - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + /** + * Used to generate source file names from the config file and its include, exclude, files rules + * and also to cache the directory stucture + */ + readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; } interface Watch { /** Synchronize with host and get updated program */ From e694b9e3bae0deb094f52652bc1a94be85e7efef Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 17:50:14 -0800 Subject: [PATCH 018/100] Update the WatchCompilerHost creation --- src/compiler/tsc.ts | 17 ++++++----------- src/compiler/watch.ts | 36 ++++++++++++++++++++++++++---------- 2 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index daccfca33b8..43211313237 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -21,7 +21,7 @@ namespace ts { return diagnostic.messageText; } - let reportDiagnostic = createDiagnosticReporter(); + let reportDiagnostic = createDiagnosticReporter(sys); function udpateReportDiagnostic(options: CompilerOptions) { if (options.pretty) { reportDiagnostic = createDiagnosticReporter(sys, /*pretty*/ true); @@ -150,33 +150,28 @@ namespace ts { return sys.exit(exitStatus); } - function createWatchCompilerHost(): WatchCompilerHost { - const watchCompilerHost = ts.createWatchCompilerHost(sys, reportDiagnostic); + function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { const compilerWithBuilderState = watchCompilerHost.afterProgramCreate; watchCompilerHost.beforeProgramCreate = enableStatistics; watchCompilerHost.afterProgramCreate = (host, program) => { compilerWithBuilderState(host, program); reportStatistics(program); }; - return watchCompilerHost; } function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { - const watchCompilerHost = createWatchCompilerHost() as WatchCompilerHostOfConfigFile; - watchCompilerHost.onConfigFileDiagnostic = reportDiagnostic; + const watchCompilerHost = ts.createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, reportDiagnostic); + updateWatchCompilationHost(watchCompilerHost); watchCompilerHost.rootFiles = configParseResult.fileNames; watchCompilerHost.options = configParseResult.options; - watchCompilerHost.configFileName = configParseResult.options.configFilePath; - watchCompilerHost.optionsToExtend = optionsToExtend; watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs; watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories; createWatch(watchCompilerHost); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { - const watchCompilerHost = createWatchCompilerHost() as WatchCompilerHostOfFilesAndCompilerOptions; - watchCompilerHost.rootFiles = rootFiles; - watchCompilerHost.options = options; + const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, reportDiagnostic); + updateWatchCompilationHost(watchCompilerHost); createWatch(watchCompilerHost); } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 0bd0fc499b7..2653be5e20d 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -15,7 +15,7 @@ namespace ts { * Create a function that reports error by writing to the system and handles the formating of the diagnostic */ /*@internal*/ - export function createDiagnosticReporter(system = sys, pretty?: boolean): DiagnosticReporter { + export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter { const host: FormatDiagnosticsHost = system === sys ? sysFormatDiagnosticsHost : { getCurrentDirectory: () => system.getCurrentDirectory(), getNewLine: () => system.newLine, @@ -266,6 +266,7 @@ namespace ts { */ /*@internal*/ export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { + cachedDirectoryStructureHost?: CachedDirectoryStructureHost; rootFiles?: string[]; options?: CompilerOptions; optionsToExtend?: CompilerOptions; @@ -298,8 +299,7 @@ namespace ts { /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ - /*@internal*/ - export function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { + function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { return { useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, @@ -325,26 +325,42 @@ namespace ts { } /** - * Create the watched program for config file + * Creates the watch compiler host from system for config file in watch mode */ - export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { + /*@internal*/ + export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfConfigFile { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile; host.onConfigFileDiagnostic = reportDiagnostic; host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); host.configFileName = configFileName; host.optionsToExtend = optionsToExtend; - return createWatch(host); + return host; + } + + /** + * Creates the watch compiler host from system for compiling root files and options in watch mode + */ + /*@internal*/ + export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { + const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; + host.rootFiles = rootFiles; + host.options = options; + return host; + } + + /** + * Create the watched program for config file + */ + export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { + return createWatch(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic)); } /** * Create the watched program for root files and compiler options */ export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; - host.rootFiles = rootFiles; - host.options = options; - return createWatch(host); + return createWatch(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic)); } /** From 8cc293635229b053632b52493988ec4ae781e812 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 18:09:10 -0800 Subject: [PATCH 019/100] Move watchFile and watchDirectory to WatchCompilerHost --- src/compiler/watch.ts | 17 +++++++---- src/compiler/watchUtilities.ts | 30 ++++++++++++------- src/harness/unittests/session.ts | 3 ++ src/server/types.ts | 4 ++- .../reference/api/tsserverlibrary.d.ts | 2 ++ tests/baselines/reference/api/typescript.d.ts | 4 +++ 6 files changed, 43 insertions(+), 17 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 2653be5e20d..210c1efe409 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -216,6 +216,11 @@ namespace ts { /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + + /** Used to watch changes in source files, missing files needed to update the program or config file */ + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; + /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; } /** @@ -310,6 +315,8 @@ namespace ts { getDirectories: getBoundFunction(system.getDirectories, system), readDirectory: getBoundFunction(system.readDirectory, system), realpath: getBoundFunction(system.realpath, system), + watchFile: getBoundFunction(system.watchFile, system), + watchDirectory: getBoundFunction(system.watchDirectory, system), system, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) }; @@ -424,7 +431,7 @@ namespace ts { const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher; if (configFileName) { - watchFile(system, configFileName, scheduleProgramReload, writeLog); + watchFile(host, configFileName, scheduleProgramReload, writeLog); } const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); @@ -581,7 +588,7 @@ namespace ts { hostSourceFile.sourceFile = sourceFile; sourceFile.version = hostSourceFile.version.toString(); if (!hostSourceFile.fileWatcher) { - hostSourceFile.fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); + hostSourceFile.fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog); } } else { @@ -594,7 +601,7 @@ namespace ts { let fileWatcher: FileWatcher; if (sourceFile) { sourceFile.version = "1"; - fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); + fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog); sourceFilesCache.set(path, { sourceFile, version: 1, fileWatcher }); } else { @@ -773,11 +780,11 @@ namespace ts { } function watchDirectory(directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags) { - return watchDirectoryWorker(system, directory, cb, flags, writeLog); + return watchDirectoryWorker(host, directory, cb, flags, writeLog); } function watchMissingFilePath(missingFilePath: Path) { - return watchFilePath(system, missingFilePath, onMissingFileChange, missingFilePath, writeLog); + return watchFilePath(host, missingFilePath, onMissingFileChange, missingFilePath, writeLog); } function onMissingFileChange(fileName: string, eventKind: FileWatcherEventKind, missingFilePath: Path) { diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index a1d22c20ac1..bf5a42b7b08 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -323,53 +323,61 @@ namespace ts { } } - export function addFileWatcher(host: System, file: string, cb: FileWatcherCallback): FileWatcher { + export interface WatchFileHost { + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; + } + + export function addFileWatcher(host: WatchFileHost, file: string, cb: FileWatcherCallback): FileWatcher { return host.watchFile(file, cb); } - export function addFileWatcherWithLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { + export function addFileWatcherWithLogging(host: WatchFileHost, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { const watcherCaption = `FileWatcher:: `; return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb); } - export function addFileWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { + export function addFileWatcherWithOnlyTriggerLogging(host: WatchFileHost, file: string, cb: FileWatcherCallback, log: (s: string) => void): FileWatcher { const watcherCaption = `FileWatcher:: `; return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb); } export type FilePathWatcherCallback = (fileName: string, eventKind: FileWatcherEventKind, filePath: Path) => void; - export function addFilePathWatcher(host: System, file: string, cb: FilePathWatcherCallback, path: Path): FileWatcher { + export function addFilePathWatcher(host: WatchFileHost, file: string, cb: FilePathWatcherCallback, path: Path): FileWatcher { return host.watchFile(file, (fileName, eventKind) => cb(fileName, eventKind, path)); } - export function addFilePathWatcherWithLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { + export function addFilePathWatcherWithLogging(host: WatchFileHost, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { const watcherCaption = `FileWatcher:: `; return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, file, cb, path); } - export function addFilePathWatcherWithOnlyTriggerLogging(host: System, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { + export function addFilePathWatcherWithOnlyTriggerLogging(host: WatchFileHost, file: string, cb: FilePathWatcherCallback, path: Path, log: (s: string) => void): FileWatcher { const watcherCaption = `FileWatcher:: `; return createWatcherWithLogging(addFileWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, file, cb, path); } - export function addDirectoryWatcher(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher { + export interface WatchDirectoryHost { + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; + } + + export function addDirectoryWatcher(host: WatchDirectoryHost, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags): FileWatcher { const recursive = (flags & WatchDirectoryFlags.Recursive) !== 0; return host.watchDirectory(directory, cb, recursive); } - export function addDirectoryWatcherWithLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { + export function addDirectoryWatcherWithLogging(host: WatchDirectoryHost, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `; return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ false, host, directory, cb, flags); } - export function addDirectoryWatcherWithOnlyTriggerLogging(host: System, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { + export function addDirectoryWatcherWithOnlyTriggerLogging(host: WatchDirectoryHost, directory: string, cb: DirectoryWatcherCallback, flags: WatchDirectoryFlags, log: (s: string) => void): FileWatcher { const watcherCaption = `DirectoryWatcher ${(flags & WatchDirectoryFlags.Recursive) !== 0 ? "recursive" : ""}:: `; return createWatcherWithLogging(addDirectoryWatcher, watcherCaption, log, /*logOnlyTrigger*/ true, host, directory, cb, flags); } type WatchCallback = (fileName: string, cbOptional1?: T, optional?: U) => void; - type AddWatch = (host: System, file: string, cb: WatchCallback, optional?: U) => FileWatcher; - function createWatcherWithLogging(addWatch: AddWatch, watcherCaption: string, log: (s: string) => void, logOnlyTrigger: boolean, host: System, file: string, cb: WatchCallback, optional?: U): FileWatcher { + type AddWatch = (host: H, file: string, cb: WatchCallback, optional?: U) => FileWatcher; + function createWatcherWithLogging(addWatch: AddWatch, watcherCaption: string, log: (s: string) => void, logOnlyTrigger: boolean, host: H, file: string, cb: WatchCallback, optional?: U): FileWatcher { const info = `PathInfo: ${file}`; if (!logOnlyTrigger) { log(`${watcherCaption}Added: ${info}`); diff --git a/src/harness/unittests/session.ts b/src/harness/unittests/session.ts index 9912294d1d2..7d71d83c754 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -2,6 +2,7 @@ namespace ts.server { let lastWrittenToHost: string; + const noopFileWatcher: FileWatcher = { close: noop }; const mockHost: ServerHost = { args: [], newLine: "\n", @@ -24,6 +25,8 @@ namespace ts.server { setImmediate: () => 0, clearImmediate: noop, createHash: Harness.mockHash, + watchFile: () => noopFileWatcher, + watchDirectory: () => noopFileWatcher }; class TestSession extends Session { diff --git a/src/server/types.ts b/src/server/types.ts index 32132ed278b..93ffeeccff1 100644 --- a/src/server/types.ts +++ b/src/server/types.ts @@ -11,6 +11,8 @@ declare namespace ts.server { type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } }; export interface ServerHost extends System { + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout(timeoutId: any): void; setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; @@ -129,4 +131,4 @@ declare namespace ts.server { createDirectory(path: string): void; watchFile?(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; } -} \ No newline at end of file +} diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 64f595664b3..ca399847583 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -4747,6 +4747,8 @@ declare namespace ts.server { }; }; interface ServerHost extends System { + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; clearTimeout(timeoutId: any): void; setImmediate(callback: (...args: any[]) => void, ...args: any[]): any; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c74fc46d60e..6f347efc46b 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3844,6 +3844,10 @@ declare namespace ts { realpath?(path: string): string; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + /** Used to watch changes in source files, missing files needed to update the program or config file */ + watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; + /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ + watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; } /** * Host to create watch with root files and options From abafddded23c4d02332ca9072b44c63ccde34e47 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 18:13:45 -0800 Subject: [PATCH 020/100] Move internal functions in the watch to separate namespace --- src/compiler/watch.ts | 118 ++++++++++++++++++++---------------------- 1 file changed, 57 insertions(+), 61 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 210c1efe409..ac9ef086fbe 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -2,9 +2,8 @@ /// /// +/*@internal*/ namespace ts { - export type DiagnosticReporter = (diagnostic: Diagnostic) => void; - const sysFormatDiagnosticsHost: FormatDiagnosticsHost = sys ? { getCurrentDirectory: () => sys.getCurrentDirectory(), getNewLine: () => sys.newLine, @@ -14,7 +13,6 @@ namespace ts { /** * Create a function that reports error by writing to the system and handles the formating of the diagnostic */ - /*@internal*/ export function createDiagnosticReporter(system: System, pretty?: boolean): DiagnosticReporter { const host: FormatDiagnosticsHost = system === sys ? sysFormatDiagnosticsHost : { getCurrentDirectory: () => system.getCurrentDirectory(), @@ -36,13 +34,11 @@ namespace ts { /** * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors */ - /*@internal*/ export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { getCurrentDirectory(): string; } /** Parses config file using System interface */ - /*@internal*/ export function parseConfigFileWithSystem(configFileName: string, optionsToExtend: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter) { const host: ParseConfigFileHost = system; host.onConfigFileDiagnostic = reportDiagnostic; @@ -56,7 +52,6 @@ namespace ts { /** * Reads the config file, reports errors if any and exits if the config file cannot be found */ - /*@internal*/ export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { let configFileText: string; try { @@ -83,6 +78,62 @@ namespace ts { return configParseResult; } + /** + * Creates the watch compiler host that can be extended with config file or root file names and options host + */ + function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { + return { + useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + getNewLine: () => system.newLine, + getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), + fileExists: getBoundFunction(system.fileExists, system), + readFile: getBoundFunction(system.readFile, system), + directoryExists: getBoundFunction(system.directoryExists, system), + getDirectories: getBoundFunction(system.getDirectories, system), + readDirectory: getBoundFunction(system.readDirectory, system), + realpath: getBoundFunction(system.realpath, system), + watchFile: getBoundFunction(system.watchFile, system), + watchDirectory: getBoundFunction(system.watchDirectory, system), + system, + afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) + }; + } + + /** + * Report error and exit + */ + export function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { + reportDiagnostic(diagnostic); + system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); + } + + /** + * Creates the watch compiler host from system for config file in watch mode + */ + export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfConfigFile { + reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); + const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile; + host.onConfigFileDiagnostic = reportDiagnostic; + host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); + host.configFileName = configFileName; + host.optionsToExtend = optionsToExtend; + return host; + } + + /** + * Creates the watch compiler host from system for compiling root files and options in watch mode + */ + export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { + const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; + host.rootFiles = rootFiles; + host.options = options; + return host; + } +} + +namespace ts { + export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + /** * Writes emitted files, source files depending on options */ @@ -301,61 +352,6 @@ namespace ts { updateRootFileNames(fileNames: string[]): void; } - /** - * Creates the watch compiler host that can be extended with config file or root file names and options host - */ - function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { - return { - useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, - getNewLine: () => system.newLine, - getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), - fileExists: getBoundFunction(system.fileExists, system), - readFile: getBoundFunction(system.readFile, system), - directoryExists: getBoundFunction(system.directoryExists, system), - getDirectories: getBoundFunction(system.getDirectories, system), - readDirectory: getBoundFunction(system.readDirectory, system), - realpath: getBoundFunction(system.realpath, system), - watchFile: getBoundFunction(system.watchFile, system), - watchDirectory: getBoundFunction(system.watchDirectory, system), - system, - afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) - }; - } - - /** - * Report error and exit - */ - /*@internal*/ - export function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { - reportDiagnostic(diagnostic); - system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - } - - /** - * Creates the watch compiler host from system for config file in watch mode - */ - /*@internal*/ - export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfConfigFile { - reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfConfigFile; - host.onConfigFileDiagnostic = reportDiagnostic; - host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); - host.configFileName = configFileName; - host.optionsToExtend = optionsToExtend; - return host; - } - - /** - * Creates the watch compiler host from system for compiling root files and options in watch mode - */ - /*@internal*/ - export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; - host.rootFiles = rootFiles; - host.options = options; - return host; - } - /** * Create the watched program for config file */ From 77e67311aad296b934b41acfec6b32542402b6c5 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 18:37:57 -0800 Subject: [PATCH 021/100] Handle setTimeout, clearTimeout, clearScreen and report watch Diagnostics --- src/compiler/watch.ts | 49 +++++++++++-------- tests/baselines/reference/api/typescript.d.ts | 6 +++ 2 files changed, 35 insertions(+), 20 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index ac9ef086fbe..fb0af18ef5b 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -94,15 +94,25 @@ namespace ts { realpath: getBoundFunction(system.realpath, system), watchFile: getBoundFunction(system.watchFile, system), watchDirectory: getBoundFunction(system.watchDirectory, system), + setTimeout: getBoundFunction(system.setTimeout, system), + clearTimeout: getBoundFunction(system.clearTimeout, system), + onWatchStatusChange, system, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) }; + + function onWatchStatusChange(diagnostic: Diagnostic, newLine: string) { + if (system.clearScreen && diagnostic.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) { + system.clearScreen(); + } + system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); + } } /** * Report error and exit */ - export function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { + function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { reportDiagnostic(diagnostic); system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } @@ -238,6 +248,8 @@ namespace ts { beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + /** If provided, called with Diagnostic message that informs about change in watch status */ + onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; // Sub set of compiler host methods to read and generate new program useCaseSensitiveFileNames(): boolean; @@ -272,6 +284,10 @@ namespace ts { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; + /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together*/ + setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; + /** If provided, will be used to reset existing delayed compilation */ + clearTimeout?(timeoutId: any): void; } /** @@ -399,8 +415,8 @@ namespace ts { const onConfigFileDiagnostic = getBoundFunction(host.onConfigFileDiagnostic, host); const readFile = getBoundFunction(host.readFile, host); const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host; - const beforeProgramCreate: WatchCompilerHost["beforeProgramCreate"] = getBoundFunction(host.beforeProgramCreate, host) || noop; - const afterProgramCreate: WatchCompilerHost["afterProgramCreate"] = getBoundFunction(host.afterProgramCreate, host) || noop; + const beforeProgramCreate = getBoundFunction(host.beforeProgramCreate, host) || noop; + const afterProgramCreate = getBoundFunction(host.afterProgramCreate, host) || noop; let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host; const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); @@ -476,8 +492,7 @@ namespace ts { resolutionCache.resolveModuleNames.bind(resolutionCache); compilerHost.resolveTypeReferenceDirectives = resolutionCache.resolveTypeReferenceDirectives.bind(resolutionCache); - clearHostScreen(); - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Starting_compilation_in_watch_mode)); + reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); synchronizeProgram(); // Update the wild card directory watch @@ -534,7 +549,7 @@ namespace ts { } afterProgramCreate(directoryStructureHost, program); - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); return program; } @@ -660,22 +675,24 @@ namespace ts { } } - function reportWatchDiagnostic(diagnostic: Diagnostic) { - system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); + function reportWatchDiagnostic(message: DiagnosticMessage) { + if (host.onWatchStatusChange) { + host.onWatchStatusChange(createCompilerDiagnostic(message), newLine); + } } // Upon detecting a file change, wait for 250ms and then perform a recompilation. This gives batch // operations (such as saving all modified files in an editor) a chance to complete before we kick // off a new compilation. function scheduleProgramUpdate() { - if (!system.setTimeout || !system.clearTimeout) { + if (!host.setTimeout || !host.clearTimeout) { return; } if (timerToUpdateProgram) { - system.clearTimeout(timerToUpdateProgram); + host.clearTimeout(timerToUpdateProgram); } - timerToUpdateProgram = system.setTimeout(updateProgram, 250); + timerToUpdateProgram = host.setTimeout(updateProgram, 250); } function scheduleProgramReload() { @@ -684,17 +701,9 @@ namespace ts { scheduleProgramUpdate(); } - function clearHostScreen() { - if (system.clearScreen) { - system.clearScreen(); - } - } - function updateProgram() { - clearHostScreen(); - timerToUpdateProgram = undefined; - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation)); + reportWatchDiagnostic(Diagnostics.File_change_detected_Starting_incremental_compilation); switch (reloadLevel) { case ConfigFileProgramReloadLevel.Partial: diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 6f347efc46b..7031f305008 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3821,6 +3821,8 @@ declare namespace ts { beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + /** If provided, called with Diagnostic message that informs about change in watch status */ + onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; useCaseSensitiveFileNames(): boolean; getNewLine(): string; getCurrentDirectory(): string; @@ -3848,6 +3850,10 @@ declare namespace ts { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; + /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together*/ + setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; + /** If provided, will be used to reset existing delayed compilation */ + clearTimeout?(timeoutId: any): void; } /** * Host to create watch with root files and options From d22ba5e9650d2434cb5200171ee45436e38c32de Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 18:50:50 -0800 Subject: [PATCH 022/100] Move the system.write to trace on WatchCompilerHost --- src/compiler/watch.ts | 10 +++++++--- tests/baselines/reference/api/typescript.d.ts | 2 ++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index fb0af18ef5b..f23f94d62c2 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -96,6 +96,7 @@ namespace ts { watchDirectory: getBoundFunction(system.watchDirectory, system), setTimeout: getBoundFunction(system.setTimeout, system), clearTimeout: getBoundFunction(system.clearTimeout, system), + trace: getBoundFunction(system.write, system), onWatchStatusChange, system, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) @@ -276,6 +277,8 @@ namespace ts { /** Symbol links resolution */ realpath?(path: string): string; + /** If provided would be used to write log about compilation */ + trace?(s: string): void; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; @@ -436,8 +439,9 @@ namespace ts { parseConfigFile(); } - const loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; - const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(newLine); } : noop; + const trace = host.trace && ((s: string) => { host.trace(s + newLine) }); + const loggingEnabled = trace && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics); + const writeLog = loggingEnabled ? trace : noop; const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher; const watchFilePath = compilerOptions.extendedDiagnostics ? ts.addFilePathWatcherWithLogging : ts.addFilePathWatcher; const watchDirectoryWorker = compilerOptions.extendedDiagnostics ? ts.addDirectoryWatcherWithLogging : ts.addDirectoryWatcher; @@ -462,7 +466,7 @@ namespace ts { getNewLine: () => newLine, fileExists, readFile, - trace: s => system.write(s + newLine), + trace, directoryExists: getBoundFunction(directoryStructureHost.directoryExists, directoryStructureHost), getDirectories: getBoundFunction(directoryStructureHost.getDirectories, directoryStructureHost), realpath: getBoundFunction(host.realpath, host), diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 7031f305008..c676aa951ac 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3844,6 +3844,8 @@ declare namespace ts { readDirectory?(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; /** Symbol links resolution */ realpath?(path: string): string; + /** If provided would be used to write log about compilation */ + trace?(s: string): void; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; /** Used to watch changes in source files, missing files needed to update the program or config file */ From c9a407e5533475463f15fb268e648dd10c5e9b0d Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 19:00:06 -0800 Subject: [PATCH 023/100] Add getDefaultLibLocation and getDefaultLibFileName and remove system from WatchCompilerHost --- src/compiler/watch.ts | 25 +++++++++---------- tests/baselines/reference/api/typescript.d.ts | 4 +-- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index f23f94d62c2..67caf3c79ca 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -86,6 +86,8 @@ namespace ts { useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), + getDefaultLibLocation, + getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), fileExists: getBoundFunction(system.fileExists, system), readFile: getBoundFunction(system.readFile, system), directoryExists: getBoundFunction(system.directoryExists, system), @@ -98,10 +100,13 @@ namespace ts { clearTimeout: getBoundFunction(system.clearTimeout, system), trace: getBoundFunction(system.write, system), onWatchStatusChange, - system, afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) }; + function getDefaultLibLocation() { + return getDirectoryPath(normalizePath(system.getExecutingFilePath())); + } + function onWatchStatusChange(diagnostic: Diagnostic, newLine: string) { if (system.clearScreen && diagnostic.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) { system.clearScreen(); @@ -242,9 +247,6 @@ namespace ts { } export interface WatchCompilerHost { - /** FS system to use */ - system: System; - /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ @@ -256,6 +258,8 @@ namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; getCurrentDirectory(): string; + getDefaultLibFileName(options: CompilerOptions): string; + getDefaultLibLocation?(): string; /** * Use to check file presence for source files and @@ -287,7 +291,7 @@ namespace ts { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; - /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together*/ + /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ clearTimeout?(timeoutId: any): void; @@ -411,7 +415,6 @@ namespace ts { let hasChangedCompilerOptions = false; // True if the compiler options have changed between compilations let hasChangedAutomaticTypeDirectiveNames = false; // True if the automatic type directives have changed - const system = host.system; const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); const currentDirectory = host.getCurrentDirectory(); const getCurrentDirectory = () => currentDirectory; @@ -439,7 +442,7 @@ namespace ts { parseConfigFile(); } - const trace = host.trace && ((s: string) => { host.trace(s + newLine) }); + const trace = host.trace && ((s: string) => { host.trace(s + newLine); }); const loggingEnabled = trace && (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics); const writeLog = loggingEnabled ? trace : noop; const watchFile = compilerOptions.extendedDiagnostics ? ts.addFileWatcherWithLogging : loggingEnabled ? ts.addFileWatcherWithOnlyTriggerLogging : ts.addFileWatcher; @@ -457,8 +460,8 @@ namespace ts { // Members for CompilerHost getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile), getSourceFileByPath: getVersionedSourceFileByPath, - getDefaultLibLocation, - getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), + getDefaultLibLocation: getBoundFunction(host.getDefaultLibLocation, host), + getDefaultLibFileName: getBoundFunction(host.getDefaultLibFileName, host), writeFile: notImplemented, getCurrentDirectory, useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, @@ -581,10 +584,6 @@ namespace ts { return directoryStructureHost.fileExists(fileName); } - function getDefaultLibLocation(): string { - return getDirectoryPath(normalizePath(system.getExecutingFilePath())); - } - function getVersionedSourceFileByPath(fileName: string, path: Path, languageVersion: ScriptTarget, onError?: (message: string) => void, shouldCreateNewSourceFile?: boolean): SourceFile { const hostSourceFile = sourceFilesCache.get(path); // No source file on the host diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c676aa951ac..521fd1e0ad6 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3815,8 +3815,6 @@ declare namespace ts { */ function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; interface WatchCompilerHost { - /** FS system to use */ - system: System; /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ @@ -3826,6 +3824,8 @@ declare namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; getCurrentDirectory(): string; + getDefaultLibFileName(options: CompilerOptions): string; + getDefaultLibLocation?(): string; /** * Use to check file presence for source files and * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well From 14f66efcc595f51adf612167bc68dc745a3265de Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Tue, 5 Dec 2017 20:23:14 -0800 Subject: [PATCH 024/100] Update the emitting file, reporting errors part of the watch api --- src/compiler/core.ts | 4 - src/compiler/tsc.ts | 43 +-- src/compiler/watch.ts | 357 ++++++++++++------ src/compiler/watchUtilities.ts | 6 +- src/server/editorServices.ts | 4 +- src/server/project.ts | 6 +- .../reference/api/tsserverlibrary.d.ts | 5 +- tests/baselines/reference/api/typescript.d.ts | 22 +- 8 files changed, 266 insertions(+), 181 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b6472987398..1c180d5bc89 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -3000,8 +3000,4 @@ namespace ts { } export function assertTypeIsNever(_: never): void { } // tslint:disable-line no-empty - - export function getBoundFunction(method: T | undefined, methodOf: {}): T | undefined { - return method && method.bind(methodOf); - } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 43211313237..ed5284e696c 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -144,17 +144,16 @@ namespace ts { enableStatistics(compilerOptions); const program = createProgram(rootFileNames, compilerOptions, compilerHost); - const exitStatus = compileProgram(program); - + const exitStatus = emitFilesAndReportErrors(program, reportDiagnostic, s => sys.write(s + sys.newLine)); reportStatistics(program); return sys.exit(exitStatus); } function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { - const compilerWithBuilderState = watchCompilerHost.afterProgramCreate; + const compileUsingBuilder = watchCompilerHost.afterProgramCreate; watchCompilerHost.beforeProgramCreate = enableStatistics; - watchCompilerHost.afterProgramCreate = (host, program) => { - compilerWithBuilderState(host, program); + watchCompilerHost.afterProgramCreate = program => { + compileUsingBuilder(program); reportStatistics(program); }; } @@ -175,40 +174,6 @@ namespace ts { createWatch(watchCompilerHost); } - function compileProgram(program: Program): ExitStatus { - let diagnostics: Diagnostic[]; - - // First get and report any syntactic errors. - diagnostics = program.getSyntacticDiagnostics().slice(); - - // If we didn't have any syntactic errors, then also try getting the global and - // semantic errors. - if (diagnostics.length === 0) { - diagnostics = program.getOptionsDiagnostics().concat(program.getGlobalDiagnostics()); - - if (diagnostics.length === 0) { - diagnostics = program.getSemanticDiagnostics().slice(); - } - } - - // Emit and report any errors we ran into. - const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); - addRange(diagnostics, emitDiagnostics); - - sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); - writeFileAndEmittedFileList(sys, program, emittedFiles); - if (emitSkipped && diagnostics.length > 0) { - // If the emitter didn't emit anything, then pass that value along. - return ExitStatus.DiagnosticsPresent_OutputsSkipped; - } - else if (diagnostics.length > 0) { - // The emitter emitted something, inform the caller if that happened in the presence - // of diagnostics or not. - return ExitStatus.DiagnosticsPresent_OutputsGenerated; - } - return ExitStatus.Success; - } - function enableStatistics(compilerOptions: CompilerOptions) { if (compilerOptions.diagnostics || compilerOptions.extendedDiagnostics) { performance.enable(); diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 67caf3c79ca..fcee17e0328 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -78,30 +78,152 @@ namespace ts { return configParseResult; } + /** + * Program structure needed to emit the files and report diagnostics + */ + export interface ProgramToEmitFilesAndReportErrors { + getCurrentDirectory(): string; + getCompilerOptions(): CompilerOptions; + getSourceFiles(): ReadonlyArray; + getSyntacticDiagnostics(): ReadonlyArray; + getOptionsDiagnostics(): ReadonlyArray; + getGlobalDiagnostics(): ReadonlyArray; + getSemanticDiagnostics(): ReadonlyArray; + emit(): EmitResult; + } + + /** + * Helper that emit files, report diagnostics and lists emitted and/or source files depending on compiler options + */ + export function emitFilesAndReportErrors(program: ProgramToEmitFilesAndReportErrors, reportDiagnostic: DiagnosticReporter, writeFileName?: (s: string) => void) { + // First get and report any syntactic errors. + const diagnostics = program.getSyntacticDiagnostics().slice(); + let reportSemanticDiagnostics = false; + + // If we didn't have any syntactic errors, then also try getting the global and + // semantic errors. + if (diagnostics.length === 0) { + addRange(diagnostics, program.getOptionsDiagnostics()); + addRange(diagnostics, program.getGlobalDiagnostics()); + + if (diagnostics.length === 0) { + reportSemanticDiagnostics = true; + } + } + + // Emit and report any errors we ran into. + const { emittedFiles, emitSkipped, diagnostics: emitDiagnostics } = program.emit(); + addRange(diagnostics, emitDiagnostics); + + if (reportSemanticDiagnostics) { + addRange(diagnostics, program.getSemanticDiagnostics()); + } + + sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); + if (writeFileName) { + const currentDir = program.getCurrentDirectory(); + forEach(emittedFiles, file => { + const filepath = getNormalizedAbsolutePath(file, currentDir); + writeFileName(`TSFILE: ${filepath}`); + }); + + if (program.getCompilerOptions().listFiles) { + forEach(program.getSourceFiles(), file => { + writeFileName(file.fileName); + }); + } + } + + if (emitSkipped && diagnostics.length > 0) { + // If the emitter didn't emit anything, then pass that value along. + return ExitStatus.DiagnosticsPresent_OutputsSkipped; + } + else if (diagnostics.length > 0) { + // The emitter emitted something, inform the caller if that happened in the presence + // of diagnostics or not. + return ExitStatus.DiagnosticsPresent_OutputsGenerated; + } + return ExitStatus.Success; + } + + /** + * Creates the function that emits files and reports errors when called with program + */ + function createEmitFilesAndReportErrorsWithBuilderUsingSystem(system: System, reportDiagnostic: DiagnosticReporter) { + const emitErrorsAndReportErrorsWithBuilder = createEmitFilesAndReportErrorsWithBuilder({ + useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + createHash: system.createHash && (s => system.createHash(s)), + writeFile, + reportDiagnostic, + writeFileName: s => system.write(s + system.newLine) + }); + let host: CachedDirectoryStructureHost | undefined; + return { + emitFilesAndReportError: (program: Program) => emitErrorsAndReportErrorsWithBuilder(program), + setHost: (cachedDirectoryStructureHost: CachedDirectoryStructureHost) => host = cachedDirectoryStructureHost + }; + + function getHost() { + return host || system; + } + + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !getHost().directoryExists(directoryPath)) { + const parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + getHost().createDirectory(directoryPath); + } + } + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { + try { + performance.mark("beforeIOWrite"); + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + + getHost().writeFile(fileName, text, writeByteOrderMark); + + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + } + } + + const noopFileWatcher: FileWatcher = { close: noop }; + /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ - function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHost { - return { + function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost { + const { emitFilesAndReportError, setHost } = createEmitFilesAndReportErrorsWithBuilderUsingSystem(system, reportDiagnostic); + const host: WatchCompilerHost = { useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, getNewLine: () => system.newLine, - getCurrentDirectory: getBoundFunction(system.getCurrentDirectory, system), + getCurrentDirectory: () => system.getCurrentDirectory(), getDefaultLibLocation, getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), - fileExists: getBoundFunction(system.fileExists, system), - readFile: getBoundFunction(system.readFile, system), - directoryExists: getBoundFunction(system.directoryExists, system), - getDirectories: getBoundFunction(system.getDirectories, system), - readDirectory: getBoundFunction(system.readDirectory, system), - realpath: getBoundFunction(system.realpath, system), - watchFile: getBoundFunction(system.watchFile, system), - watchDirectory: getBoundFunction(system.watchDirectory, system), - setTimeout: getBoundFunction(system.setTimeout, system), - clearTimeout: getBoundFunction(system.clearTimeout, system), - trace: getBoundFunction(system.write, system), + fileExists: path => system.fileExists(path), + readFile: (path, encoding) => system.readFile(path, encoding), + directoryExists: path => system.directoryExists(path), + getDirectories: path => system.getDirectories(path), + readDirectory: (path, extensions, exclude, include, depth) => system.readDirectory(path, extensions, exclude, include, depth), + realpath: system.realpath && (path => system.realpath(path)), + watchFile: system.watchFile ? ((path, callback, pollingInterval) => system.watchFile(path, callback, pollingInterval)) : () => noopFileWatcher, + watchDirectory: system.watchDirectory ? ((path, callback, recursive) => system.watchDirectory(path, callback, recursive)) : () => noopFileWatcher, + setTimeout: system.setTimeout ? ((callback, ms, ...args: any[]) => system.setTimeout.call(system, callback, ms, ...args)) : noop, + clearTimeout: system.clearTimeout ? (timeoutId => system.clearTimeout(timeoutId)) : noop, + trace: s => system.write(s), onWatchStatusChange, - afterProgramCreate: createProgramCompilerWithBuilderState(system, reportDiagnostic) + createDirectory: path => system.createDirectory(path), + writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), + onCachedDirectoryStructureHostCreate: host => setHost(host), + afterProgramCreate: emitFilesAndReportError, }; + return host; function getDefaultLibLocation() { return getDirectoryPath(normalizePath(system.getExecutingFilePath())); @@ -140,7 +262,7 @@ namespace ts { * Creates the watch compiler host from system for compiling root files and options in watch mode */ export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic) as WatchCompilerHostOfFilesAndCompilerOptions; + const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system)) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; return host; @@ -150,99 +272,75 @@ namespace ts { namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; - /** - * Writes emitted files, source files depending on options - */ - /*@internal*/ - export function writeFileAndEmittedFileList(system: System, program: Program, emittedFiles: string[]) { - const currentDir = system.getCurrentDirectory(); - forEach(emittedFiles, file => { - const filepath = getNormalizedAbsolutePath(file, currentDir); - system.write(`TSFILE: ${filepath}${system.newLine}`); - }); + interface BuilderProgram extends ProgramToEmitFilesAndReportErrors { + updateProgram(program: Program): void; + } - if (program.getCompilerOptions().listFiles) { - forEach(program.getSourceFiles(), file => { - system.write(file.fileName + system.newLine); - }); + function createBuilderProgram(host: BuilderEmitHost): BuilderProgram { + const builder = createEmitAndSemanticDiagnosticsBuilder({ + getCanonicalFileName: createGetCanonicalFileName(host.useCaseSensitiveFileNames()), + computeHash: host.createHash ? host.createHash : identity + }); + let program: Program; + return { + getCurrentDirectory: () => program.getCurrentDirectory(), + getCompilerOptions: () => program.getCompilerOptions(), + getSourceFiles: () => program.getSourceFiles(), + getSyntacticDiagnostics: () => program.getSyntacticDiagnostics(), + getOptionsDiagnostics: () => program.getOptionsDiagnostics(), + getGlobalDiagnostics: () => program.getGlobalDiagnostics(), + getSemanticDiagnostics: () => builder.getSemanticDiagnostics(program), + emit, + updateProgram + }; + + function updateProgram(p: Program) { + program = p; + builder.updateProgram(p); + } + + function emit(): EmitResult { + // Emit and report any errors we ran into. + let sourceMaps: SourceMapData[]; + let emitSkipped: boolean; + let diagnostics: Diagnostic[]; + let emittedFiles: string[]; + + let affectedEmitResult: AffectedFileResult; + while (affectedEmitResult = builder.emitNextAffectedFile(program, host.writeFile)) { + emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; + diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); + emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); + sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); + } + return { + emitSkipped, + diagnostics, + emittedFiles, + sourceMaps + }; } } /** - * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files + * Host needed to emit files and report errors using builder */ - export function createProgramCompilerWithBuilderState(system = sys, reportDiagnostic?: DiagnosticReporter) { - reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - const builder = createEmitAndSemanticDiagnosticsBuilder({ - getCanonicalFileName: createGetCanonicalFileName(system.useCaseSensitiveFileNames), - computeHash: getBoundFunction(system.createHash, system) || identity - }); + export interface BuilderEmitHost { + useCaseSensitiveFileNames(): boolean; + createHash?: (data: string) => string; + writeFile: WriteFileCallback; + reportDiagnostic: DiagnosticReporter; + writeFileName?: (s: string) => void; + } - return (host: DirectoryStructureHost, program: Program) => { - builder.updateProgram(program); - - // First get and report any syntactic errors. - const diagnostics = program.getSyntacticDiagnostics().slice(); - let reportSemanticDiagnostics = false; - - // If we didn't have any syntactic errors, then also try getting the global and - // semantic errors. - if (diagnostics.length === 0) { - addRange(diagnostics, program.getOptionsDiagnostics()); - addRange(diagnostics, program.getGlobalDiagnostics()); - - if (diagnostics.length === 0) { - reportSemanticDiagnostics = true; - } - } - - // Emit and report any errors we ran into. - const emittedFiles: string[] = program.getCompilerOptions().listEmittedFiles ? [] : undefined; - let sourceMaps: SourceMapData[]; - let emitSkipped: boolean; - - let affectedEmitResult: AffectedFileResult; - while (affectedEmitResult = builder.emitNextAffectedFile(program, writeFile)) { - emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; - addRange(diagnostics, affectedEmitResult.result.diagnostics); - sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); - } - - if (reportSemanticDiagnostics) { - addRange(diagnostics, builder.getSemanticDiagnostics(program)); - } - - sortAndDeduplicateDiagnostics(diagnostics).forEach(reportDiagnostic); - writeFileAndEmittedFileList(system, program, emittedFiles); - - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { - const parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - host.createDirectory(directoryPath); - } - } - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { - try { - performance.mark("beforeIOWrite"); - ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - - host.writeFile(fileName, text, writeByteOrderMark); - - performance.mark("afterIOWrite"); - performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); - - if (emittedFiles) { - emittedFiles.push(fileName); - } - } - catch (e) { - if (onError) { - onError(e.message); - } - } - } + /** + * Creates the function that reports the program errors and emit files every time it is called with argument as program + */ + export function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost) { + const builderProgram = createBuilderProgram(host); + return (program: Program) => { + builderProgram.updateProgram(program); + emitFilesAndReportErrors(builderProgram, host.reportDiagnostic, host.writeFileName); }; } @@ -250,7 +348,7 @@ namespace ts { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + afterProgramCreate?(program: Program): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; @@ -297,6 +395,14 @@ namespace ts { clearTimeout?(timeoutId: any): void; } + /** Internal interface used to wire emit through same host */ + /*@internal*/ + export interface WatchCompilerHost { + createDirectory?(path: string): void; + writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; + onCachedDirectoryStructureHostCreate?(host: CachedDirectoryStructureHost): void; + } + /** * Host to create watch with root files and options */ @@ -315,12 +421,12 @@ namespace ts { /** * Reports the diagnostics in reading/writing or parsing of the config file */ - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + onConfigFileDiagnostic: DiagnosticReporter; /** * Reports unrecoverable error when parsing config file */ - onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; } /** @@ -345,7 +451,6 @@ namespace ts { */ /*@internal*/ export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { - cachedDirectoryStructureHost?: CachedDirectoryStructureHost; rootFiles?: string[]; options?: CompilerOptions; optionsToExtend?: CompilerOptions; @@ -418,23 +523,23 @@ namespace ts { const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); const currentDirectory = host.getCurrentDirectory(); const getCurrentDirectory = () => currentDirectory; - const onConfigFileDiagnostic = getBoundFunction(host.onConfigFileDiagnostic, host); - const readFile = getBoundFunction(host.readFile, host); + const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding); const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host; - const beforeProgramCreate = getBoundFunction(host.beforeProgramCreate, host) || noop; - const afterProgramCreate = getBoundFunction(host.afterProgramCreate, host) || noop; let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host; const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); + if (cachedDirectoryStructureHost && host.onCachedDirectoryStructureHostCreate) { + host.onCachedDirectoryStructureHostCreate(cachedDirectoryStructureHost); + } const directoryStructureHost: DirectoryStructureHost = cachedDirectoryStructureHost || host; const parseConfigFileHost: ParseConfigFileHost = { useCaseSensitiveFileNames, - readDirectory: getBoundFunction(directoryStructureHost.readDirectory, directoryStructureHost), - fileExists: getBoundFunction(directoryStructureHost.fileExists, directoryStructureHost), + readDirectory: (path, extensions, exclude, include, depth) => directoryStructureHost.readDirectory(path, extensions, exclude, include, depth), + fileExists: path => host.fileExists(path), readFile, getCurrentDirectory, - onConfigFileDiagnostic, - onUnRecoverableConfigFileDiagnostic: getBoundFunction(host.onUnRecoverableConfigFileDiagnostic, host) + onConfigFileDiagnostic: host.onConfigFileDiagnostic, + onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic }; // From tsc we want to get already parsed result and hence check for rootFileNames @@ -460,8 +565,8 @@ namespace ts { // Members for CompilerHost getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile), getSourceFileByPath: getVersionedSourceFileByPath, - getDefaultLibLocation: getBoundFunction(host.getDefaultLibLocation, host), - getDefaultLibFileName: getBoundFunction(host.getDefaultLibFileName, host), + getDefaultLibLocation: host.getDefaultLibLocation && (() => host.getDefaultLibLocation()), + getDefaultLibFileName: options => host.getDefaultLibFileName(options), writeFile: notImplemented, getCurrentDirectory, useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, @@ -470,9 +575,9 @@ namespace ts { fileExists, readFile, trace, - directoryExists: getBoundFunction(directoryStructureHost.directoryExists, directoryStructureHost), - getDirectories: getBoundFunction(directoryStructureHost.getDirectories, directoryStructureHost), - realpath: getBoundFunction(host.realpath, host), + directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists(path)), + getDirectories: directoryStructureHost.getDirectories && (path => directoryStructureHost.getDirectories(path)), + realpath: host.realpath && (s => host.realpath(s)), onReleaseOldSourceFile, // Members for ResolutionCacheHost toPath, @@ -495,8 +600,8 @@ namespace ts { ); // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names compilerHost.resolveModuleNames = host.resolveModuleNames ? - host.resolveModuleNames.bind(host) : - resolutionCache.resolveModuleNames.bind(resolutionCache); + ((moduleNames, containingFile, reusedNames) => host.resolveModuleNames(moduleNames, containingFile, reusedNames)) : + ((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames)); compilerHost.resolveTypeReferenceDirectives = resolutionCache.resolveTypeReferenceDirectives.bind(resolutionCache); reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); @@ -524,7 +629,9 @@ namespace ts { return program; } - beforeProgramCreate(compilerOptions); + if (host.beforeProgramCreate) { + host.beforeProgramCreate(compilerOptions); + } // Compile the program const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program; @@ -555,7 +662,9 @@ namespace ts { missingFilePathsRequestedForRelease = undefined; } - afterProgramCreate(directoryStructureHost, program); + if (host.afterProgramCreate) { + host.afterProgramCreate(program); + } reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); return program; } @@ -722,7 +831,7 @@ namespace ts { function reloadFileNamesFromConfigFile() { const result = getFileNamesFromConfigSpecs(configFileSpecs, getDirectoryPath(configFileName), compilerOptions, parseConfigFileHost); if (!configFileSpecs.filesSpecs && result.fileNames.length === 0) { - onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); + host.onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); } rootFileNames = result.fileNames; diff --git a/src/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index bf5a42b7b08..24c9737fb26 100644 --- a/src/compiler/watchUtilities.ts +++ b/src/compiler/watchUtilities.ts @@ -49,12 +49,12 @@ namespace ts { return { useCaseSensitiveFileNames, fileExists, - readFile: getBoundFunction(host.readFile, host), + readFile: (path, encoding) => host.readFile(path, encoding), directoryExists: host.directoryExists && directoryExists, getDirectories, readDirectory, - createDirectory, - writeFile, + createDirectory: host.createDirectory && createDirectory, + writeFile: host.writeFile && writeFile, addOrDeleteFileOrDirectory, addOrDeleteFile, clearCache diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index bafe90a59cb..6afeae16ef5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1765,11 +1765,11 @@ namespace ts.server { return this.getOrCreateScriptInfoWorker(fileName, currentDirectory, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent); } - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) { + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { fileExists(path: string): boolean; }) { return this.getOrCreateScriptInfoWorker(fileName, this.currentDirectory, openedByClient, fileContent, scriptKind, hasMixedContent, hostToQueryFileExistsOn); } - private getOrCreateScriptInfoWorker(fileName: NormalizedPath, currentDirectory: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost) { + private getOrCreateScriptInfoWorker(fileName: NormalizedPath, currentDirectory: string, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { fileExists(path: string): boolean; }) { Debug.assert(fileContent === undefined || openedByClient, "ScriptInfo needs to be opened by client to be able to set its user defined content"); const path = normalizedPathToPath(fileName, currentDirectory, this.toCanonicalFileName); let info = this.getScriptInfoForPath(path); diff --git a/src/server/project.ts b/src/server/project.ts index 9ae1b1d0b6e..26d8aba0bd5 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -201,6 +201,9 @@ namespace ts.server { /*@internal*/ readonly currentDirectory: string; + /*@internal*/ + public directoryStructureHost: DirectoryStructureHost; + /*@internal*/ constructor( /*@internal*/readonly projectName: string, @@ -211,8 +214,9 @@ namespace ts.server { languageServiceEnabled: boolean, private compilerOptions: CompilerOptions, public compileOnSaveEnabled: boolean, - /*@internal*/public directoryStructureHost: DirectoryStructureHost, + directoryStructureHost: DirectoryStructureHost, currentDirectory: string | undefined) { + this.directoryStructureHost = directoryStructureHost; this.currentDirectory = this.projectService.getNormalizedAbsolutePath(currentDirectory || ""); this.cancellationToken = new ThrottledCancellationToken(this.projectService.cancellationToken, this.projectService.throttleWaitMilliseconds); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index ca399847583..4abb45a11e4 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7270,7 +7270,6 @@ declare namespace ts.server { private documentRegistry; private compilerOptions; compileOnSaveEnabled: boolean; - directoryStructureHost: DirectoryStructureHost; private rootFiles; private rootFilesMap; private program; @@ -7741,7 +7740,9 @@ declare namespace ts.server { getScriptInfo(uncheckedFileName: string): ScriptInfo; private watchClosedScriptInfo(info); private stopWatchingScriptInfo(info); - getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: DirectoryStructureHost): ScriptInfo; + getOrCreateScriptInfoForNormalizedPath(fileName: NormalizedPath, openedByClient: boolean, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, hostToQueryFileExistsOn?: { + fileExists(path: string): boolean; + }): ScriptInfo; private getOrCreateScriptInfoWorker(fileName, currentDirectory, openedByClient, fileContent?, scriptKind?, hasMixedContent?, hostToQueryFileExistsOn?); /** * This gets the script info for the normalized path. If the path is not rooted disk path then the open script info with project root context is preferred diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 521fd1e0ad6..eed0c7cc5d8 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3811,14 +3811,24 @@ declare namespace ts { declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; /** - * Creates the function that compiles the program by maintaining the builder for the program and reports the errors and emits files + * Host needed to emit files and report errors using builder */ - function createProgramCompilerWithBuilderState(system?: System, reportDiagnostic?: DiagnosticReporter): (host: DirectoryStructureHost, program: Program) => void; + interface BuilderEmitHost { + useCaseSensitiveFileNames(): boolean; + createHash?: (data: string) => string; + writeFile: WriteFileCallback; + reportDiagnostic: DiagnosticReporter; + writeFileName?: (s: string) => void; + } + /** + * Creates the function that reports the program errors and emit files every time it is called with argument as program + */ + function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost): (program: Program) => void; interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(host: DirectoryStructureHost, program: Program): void; + afterProgramCreate?(program: Program): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; useCaseSensitiveFileNames(): boolean; @@ -3852,7 +3862,7 @@ declare namespace ts { watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ watchDirectory(path: string, callback: DirectoryWatcherCallback, recursive?: boolean): FileWatcher; - /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together*/ + /** If provided, will be used to set delayed compilation, so that multiple changes in short span are compiled together */ setTimeout?(callback: (...args: any[]) => void, ms: number, ...args: any[]): any; /** If provided, will be used to reset existing delayed compilation */ clearTimeout?(timeoutId: any): void; @@ -3873,11 +3883,11 @@ declare namespace ts { /** * Reports the diagnostics in reading/writing or parsing of the config file */ - onConfigFileDiagnostic(diagnostic: Diagnostic): void; + onConfigFileDiagnostic: DiagnosticReporter; /** * Reports unrecoverable error when parsing config file */ - onUnRecoverableConfigFileDiagnostic(diagnostic: Diagnostic): void; + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; } /** * Host to create watch with config file From a21b07405570dae526eb4f54e9bbd714d0a3b4cd Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 6 Dec 2017 13:59:53 -0800 Subject: [PATCH 025/100] Update the builder to take options aligning with the WatchCompilerHost --- src/compiler/builder.ts | 25 +++++++++++++++---- src/compiler/watch.ts | 9 ++----- src/harness/unittests/builder.ts | 10 ++------ src/server/project.ts | 4 +-- .../reference/api/tsserverlibrary.d.ts | 10 ++++++-- tests/baselines/reference/api/typescript.d.ts | 14 +++++++---- 6 files changed, 43 insertions(+), 29 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index ddbb015a2ee..c25cbb0d5a4 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -66,6 +66,15 @@ namespace ts { export function createBuilder(options: BuilderOptions, builderType: BuilderType.SemanticDiagnosticsBuilder): SemanticDiagnosticsBuilder; export function createBuilder(options: BuilderOptions, builderType: BuilderType.EmitAndSemanticDiagnosticsBuilder): EmitAndSemanticDiagnosticsBuilder; export function createBuilder(options: BuilderOptions, builderType: BuilderType) { + /** + * Create the canonical file name for identity + */ + const getCanonicalFileName = createGetCanonicalFileName(options.useCaseSensitiveFileNames()); + /** + * Computing hash to for signature verification + */ + const computeHash = options.createHash || identity; + /** * Information of the file eg. its version, signature etc */ @@ -572,7 +581,7 @@ namespace ts { else { const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { - latestSignature = options.computeHash(emitOutput.outputFiles[0].text); + latestSignature = computeHash(emitOutput.outputFiles[0].text); } else { latestSignature = prevSignature; @@ -609,7 +618,7 @@ namespace ts { // Handle triple slash references if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { for (const referencedFile of sourceFile.referencedFiles) { - const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, options.getCanonicalFileName); + const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); addReferencedFile(referencedPath); } } @@ -622,7 +631,7 @@ namespace ts { } const fileName = resolvedTypeReferenceDirective.resolvedFileName; - const typeFilePath = toPath(fileName, sourceFileDirectory, options.getCanonicalFileName); + const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); addReferencedFile(typeFilePath); }); } @@ -738,8 +747,14 @@ namespace ts { export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; export interface BuilderOptions { - getCanonicalFileName: (fileName: string) => string; - computeHash: (data: string) => string; + /** + * return true if file names are treated with case sensitivity + */ + useCaseSensitiveFileNames(): boolean; + /** + * If provided this would be used this hash instead of actual file shape text for detecting changes + */ + createHash?: (data: string) => string; } /** diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index fcee17e0328..6770c036b10 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -277,10 +277,7 @@ namespace ts { } function createBuilderProgram(host: BuilderEmitHost): BuilderProgram { - const builder = createEmitAndSemanticDiagnosticsBuilder({ - getCanonicalFileName: createGetCanonicalFileName(host.useCaseSensitiveFileNames()), - computeHash: host.createHash ? host.createHash : identity - }); + const builder = createEmitAndSemanticDiagnosticsBuilder(host); let program: Program; return { getCurrentDirectory: () => program.getCurrentDirectory(), @@ -325,9 +322,7 @@ namespace ts { /** * Host needed to emit files and report errors using builder */ - export interface BuilderEmitHost { - useCaseSensitiveFileNames(): boolean; - createHash?: (data: string) => string; + export interface BuilderEmitHost extends BuilderOptions { writeFile: WriteFileCallback; reportDiagnostic: DiagnosticReporter; writeFileName?: (s: string) => void; diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index 8ad7f4da3f9..a6353d9c0c3 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -81,10 +81,7 @@ namespace ts { }); function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { - const builder = createEmitAndSemanticDiagnosticsBuilder({ - getCanonicalFileName: identity, - computeHash: identity - }); + const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); return fileNames => { const program = getProgram(); builder.updateProgram(program); @@ -97,10 +94,7 @@ namespace ts { } function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray, cancelAfterEmitLength?: number) => void { - const builder = createEmitAndSemanticDiagnosticsBuilder({ - getCanonicalFileName: identity, - computeHash: identity - }); + const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); let cancel = false; const cancellationToken: CancellationToken = { isCancellationRequested: () => cancel, diff --git a/src/server/project.ts b/src/server/project.ts index 26d8aba0bd5..1fa12e50120 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -462,8 +462,8 @@ namespace ts.server { this.updateGraph(); if (!this.builder) { this.builder = createInternalBuilder({ - getCanonicalFileName: this.projectService.toCanonicalFileName, - computeHash: data => this.projectService.host.createHash(data) + useCaseSensitiveFileNames: () => this.useCaseSensitiveFileNames(), + createHash: data => this.projectService.host.createHash(data) }); } this.builder.updateProgram(this.program); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 4abb45a11e4..c613293c9ba 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3772,8 +3772,14 @@ declare namespace ts { affected: SourceFile | Program; } | undefined; interface BuilderOptions { - getCanonicalFileName: (fileName: string) => string; - computeHash: (data: string) => string; + /** + * return true if file names are treated with case sensitivity + */ + useCaseSensitiveFileNames(): boolean; + /** + * If provided this would be used this hash instead of actual file shape text for detecting changes + */ + createHash?: (data: string) => string; } /** * Builder to manage the program state changes diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index eed0c7cc5d8..c9d4e3dd0a3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3719,8 +3719,14 @@ declare namespace ts { affected: SourceFile | Program; } | undefined; interface BuilderOptions { - getCanonicalFileName: (fileName: string) => string; - computeHash: (data: string) => string; + /** + * return true if file names are treated with case sensitivity + */ + useCaseSensitiveFileNames(): boolean; + /** + * If provided this would be used this hash instead of actual file shape text for detecting changes + */ + createHash?: (data: string) => string; } /** * Builder to manage the program state changes @@ -3813,9 +3819,7 @@ declare namespace ts { /** * Host needed to emit files and report errors using builder */ - interface BuilderEmitHost { - useCaseSensitiveFileNames(): boolean; - createHash?: (data: string) => string; + interface BuilderEmitHost extends BuilderOptions { writeFile: WriteFileCallback; reportDiagnostic: DiagnosticReporter; writeFileName?: (s: string) => void; From 39bf33d8414b56a5642644e0086dd63bd67df477 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 10:02:02 -0800 Subject: [PATCH 026/100] Few renames --- src/compiler/builder.ts | 42 +++++++++---------- src/compiler/watch.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 6 +-- tests/baselines/reference/api/typescript.d.ts | 8 ++-- 4 files changed, 29 insertions(+), 29 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index c25cbb0d5a4..9c7707bd1d8 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -28,14 +28,14 @@ namespace ts { /** * Create the internal builder to get files affected by sourceFile */ - export function createInternalBuilder(options: BuilderOptions): InternalBuilder { - return createBuilder(options, BuilderType.InternalBuilder); + export function createInternalBuilder(host: BuilderHost): InternalBuilder { + return createBuilder(host, BuilderKind.BuilderKindInternal); } - export enum BuilderType { - InternalBuilder, - SemanticDiagnosticsBuilder, - EmitAndSemanticDiagnosticsBuilder + export enum BuilderKind { + BuilderKindInternal, + BuilderKindSemanticDiagnostics, + BuilderKindEmitAndSemanticDiagnostics } /** @@ -62,18 +62,18 @@ namespace ts { return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); } - export function createBuilder(options: BuilderOptions, builderType: BuilderType.InternalBuilder): InternalBuilder; - export function createBuilder(options: BuilderOptions, builderType: BuilderType.SemanticDiagnosticsBuilder): SemanticDiagnosticsBuilder; - export function createBuilder(options: BuilderOptions, builderType: BuilderType.EmitAndSemanticDiagnosticsBuilder): EmitAndSemanticDiagnosticsBuilder; - export function createBuilder(options: BuilderOptions, builderType: BuilderType) { + export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindInternal): InternalBuilder; + export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; + export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; + export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { /** * Create the canonical file name for identity */ - const getCanonicalFileName = createGetCanonicalFileName(options.useCaseSensitiveFileNames()); + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); /** * Computing hash to for signature verification */ - const computeHash = options.createHash || identity; + const computeHash = host.createHash || identity; /** * Information of the file eg. its version, signature etc @@ -141,12 +141,12 @@ namespace ts { */ const seenAffectedFiles = createMap(); - switch (builderType) { - case BuilderType.InternalBuilder: + switch (builderKind) { + case BuilderKind.BuilderKindInternal: return getInternalBuilder(); - case BuilderType.SemanticDiagnosticsBuilder: + case BuilderKind.BuilderKindSemanticDiagnostics: return getSemanticDiagnosticsBuilder(); - case BuilderType.EmitAndSemanticDiagnosticsBuilder: + case BuilderKind.BuilderKindEmitAndSemanticDiagnostics: return getEmitAndSemanticDiagnosticsBuilder(); default: notImplemented(); @@ -746,7 +746,7 @@ namespace ts { export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; - export interface BuilderOptions { + export interface BuilderHost { /** * return true if file names are treated with case sensitivity */ @@ -813,15 +813,15 @@ namespace ts { /** * Create the builder to manage semantic diagnostics and cache them */ - export function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder { - return createBuilder(options, BuilderType.SemanticDiagnosticsBuilder); + export function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder { + return createBuilder(host, BuilderKind.BuilderKindSemanticDiagnostics); } /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - export function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder { - return createBuilder(options, BuilderType.EmitAndSemanticDiagnosticsBuilder); + export function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder { + return createBuilder(host, BuilderKind.BuilderKindEmitAndSemanticDiagnostics); } } diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 6770c036b10..1c5a0ad5d99 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -322,7 +322,7 @@ namespace ts { /** * Host needed to emit files and report errors using builder */ - export interface BuilderEmitHost extends BuilderOptions { + export interface BuilderEmitHost extends BuilderHost { writeFile: WriteFileCallback; reportDiagnostic: DiagnosticReporter; writeFileName?: (s: string) => void; diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 97a24adb74d..d7f3821a25d 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3771,7 +3771,7 @@ declare namespace ts { result: T; affected: SourceFile | Program; } | undefined; - interface BuilderOptions { + interface BuilderHost { /** * return true if file names are treated with case sensitivity */ @@ -3831,12 +3831,12 @@ declare namespace ts { /** * Create the builder to manage semantic diagnostics and cache them */ - function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder; + function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder; /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder; + function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c9d4e3dd0a3..06c75e843c0 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3718,7 +3718,7 @@ declare namespace ts { result: T; affected: SourceFile | Program; } | undefined; - interface BuilderOptions { + interface BuilderHost { /** * return true if file names are treated with case sensitivity */ @@ -3778,12 +3778,12 @@ declare namespace ts { /** * Create the builder to manage semantic diagnostics and cache them */ - function createSemanticDiagnosticsBuilder(options: BuilderOptions): SemanticDiagnosticsBuilder; + function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder; /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - function createEmitAndSemanticDiagnosticsBuilder(options: BuilderOptions): EmitAndSemanticDiagnosticsBuilder; + function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; } declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; @@ -3819,7 +3819,7 @@ declare namespace ts { /** * Host needed to emit files and report errors using builder */ - interface BuilderEmitHost extends BuilderOptions { + interface BuilderEmitHost extends BuilderHost { writeFile: WriteFileCallback; reportDiagnostic: DiagnosticReporter; writeFileName?: (s: string) => void; From 4c21cbf145b3dab90c6e5c6b6f2642125ce7b312 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 11:47:49 -0800 Subject: [PATCH 027/100] Create builderState so that when FilesAffectedBy is only api needed, we arent tracking changed files --- src/compiler/builderState.ts | 431 +++++++++++++++++++++++++++++++++++ src/compiler/program.ts | 1 - src/server/project.ts | 14 +- 3 files changed, 440 insertions(+), 6 deletions(-) create mode 100644 src/compiler/builderState.ts diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts new file mode 100644 index 00000000000..f6706195a75 --- /dev/null +++ b/src/compiler/builderState.ts @@ -0,0 +1,431 @@ +/// +namespace ts { + export interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + + export interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } +} + +/*@internal*/ +namespace ts { + export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, + cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput { + const outputFiles: OutputFile[] = []; + const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); + return { outputFiles, emitSkipped: emitResult.emitSkipped }; + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { + outputFiles.push({ name: fileName, writeByteOrderMark, text }); + } + } + + /** + * Information about the source file: Its version and optional signature from last emit + */ + interface FileInfo { + version: string; + signature?: string; + } + + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + type ReferencedSet = ReadonlyMap; + + function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { + if (map1 === undefined) { + return map2 === undefined; + } + if (map2 === undefined) { + return map1 === undefined; + } + // Has same size and every key is present in both maps + return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); + } + + export interface BuilderStateHost { + /** + * true if file names are treated with case sensitivity + */ + useCaseSensitiveFileNames: boolean; + /** + * if provided this would be used this hash instead of actual file shape text for detecting changes + */ + createHash?: (data: string) => string; + /** + * Called when programState is initialized, indicating if isModuleEmit is changed + */ + onUpdateProgramInitialized(isModuleEmitChanged: boolean): void; + onSourceFileAdd(path: Path): void; + onSourceFileChanged(path: Path): void; + onSourceFileRemoved(path: Path): void; + } + + export interface BuilderState { + /** + * Updates the program in the builder to represent new state + */ + updateProgram(newProgram: Program): void; + /** + * Gets the files affected by the file path + */ + getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken, cacheToUpdateSignature?: Map): ReadonlyArray; + } + + export function createBuilderState(host: BuilderStateHost): BuilderState { + /** + * Create the canonical file name for identity + */ + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); + /** + * Computing hash to for signature verification + */ + const computeHash = host.createHash || identity; + + /** + * Information of the file eg. its version, signature etc + */ + const fileInfos = createMap(); + + /** + * true if module emit is enabled + */ + let isModuleEmit: boolean; + + /** + * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled + * Otherwise undefined + */ + let referencedMap: Map | undefined; + + /** + * Get the files affected by the source file. + * This is dependent on whether its a module emit or not and hence function expression + */ + let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) => ReadonlyArray; + + /** + * Map of files that have already called update signature. + * That means hence forth these files are assumed to have + * no change in their signature for this version of the program + */ + const hasCalledUpdateShapeSignature = createMap(); + + /** + * Cache of all files excluding default library file for the current program + */ + let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; + + return { + updateProgram, + getFilesAffectedBy, + }; + + /** + * Update current state to reflect new program + * Updates changed files, references, file infos etc + */ + function updateProgram(newProgram: Program) { + const newProgramHasModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; + const oldReferencedMap = referencedMap; + const isModuleEmitChanged = isModuleEmit !== newProgramHasModuleEmit; + if (isModuleEmitChanged) { + // Changes in the module emit, clear out everything and initialize as if first time + + // Clear file information + fileInfos.clear(); + + // Update the reference map creation + referencedMap = newProgramHasModuleEmit ? createMap() : undefined; + + // Update the module emit + isModuleEmit = newProgramHasModuleEmit; + getEmitDependentFilesAffectedBy = isModuleEmit ? + getFilesAffectedByUpdatedShapeWhenModuleEmit : + getFilesAffectedByUpdatedShapeWhenNonModuleEmit; + } + host.onUpdateProgramInitialized(isModuleEmitChanged); + + // Clear datas that cant be retained beyond previous state + hasCalledUpdateShapeSignature.clear(); + allFilesExcludingDefaultLibraryFile = undefined; + + // Create the reference map and update changed files + for (const sourceFile of newProgram.getSourceFiles()) { + const version = sourceFile.version; + const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); + const oldInfo = fileInfos.get(sourceFile.path); + let oldReferences: ReferencedSet; + + // Register changed file if its new file or we arent reusing old state + if (!oldInfo) { + // New file: Set the file info + fileInfos.set(sourceFile.path, { version }); + host.onSourceFileAdd(sourceFile.path); + } + // versions dont match + else if (oldInfo.version !== version || + // Referenced files changed + !hasSameKeys(newReferences, (oldReferences = oldReferencedMap && oldReferencedMap.get(sourceFile.path))) || + // Referenced file was deleted in the new program + newReferences && forEachEntry(newReferences, (_value, path) => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { + + // Changed file: Update the version, set as changed file + oldInfo.version = version; + host.onSourceFileChanged(sourceFile.path); + } + + // Set the references + if (newReferences) { + referencedMap.set(sourceFile.path, newReferences); + } + else if (referencedMap) { + referencedMap.delete(sourceFile.path); + } + } + + // For removed files, remove the semantic diagnostics and file info + if (fileInfos.size > newProgram.getSourceFiles().length) { + fileInfos.forEach((_value, path) => { + if (!newProgram.getSourceFileByPath(path as Path)) { + fileInfos.delete(path); + host.onSourceFileRemoved(path as Path); + if (referencedMap) { + referencedMap.delete(path); + } + } + }); + } + } + + /** + * Gets the files affected by the path from the program + */ + function getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, cacheToUpdateSignature?: Map): ReadonlyArray { + // Since the operation could be cancelled, the signatures are always stored in the cache + // They will be commited once it is safe to use them + // eg when calling this api from tsserver, if there is no cancellation of the operation + // In the other cases the affected files signatures are commited only after the iteration through the result is complete + const signatureCache = cacheToUpdateSignature || createMap(); + const sourceFile = programOfThisState.getSourceFileByPath(path); + if (!sourceFile) { + return emptyArray; + } + + if (!updateShapeSignature(programOfThisState, sourceFile, signatureCache, cancellationToken)) { + return [sourceFile]; + } + + const result = getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, signatureCache, cancellationToken); + if (!cacheToUpdateSignature) { + // Commit all the signatures in the signature cache + updateSignaturesFromCache(signatureCache); + } + return result; + } + + /** + * Updates the signatures from the cache + * This should be called whenever it is safe to commit the state of the builder + */ + function updateSignaturesFromCache(signatureCache: Map) { + signatureCache.forEach((signature, path) => { + fileInfos.get(path).signature = signature; + hasCalledUpdateShapeSignature.set(path, true); + }); + } + + /** + * For script files that contains only ambient external modules, although they are not actually external module files, + * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, + * there are no point to rebuild all script files if these special files have changed. However, if any statement + * in the file is not ambient external module, we treat it as a regular script file. + */ + function containsOnlyAmbientModules(sourceFile: SourceFile) { + for (const statement of sourceFile.statements) { + if (!isModuleWithStringLiteralName(statement)) { + return false; + } + } + return true; + } + + /** + * Returns if the shape of the signature has changed since last emit + * Note that it also updates the current signature as the latest signature for the file + */ + function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { + Debug.assert(!!sourceFile); + + // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate + if (hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { + return false; + } + + const info = fileInfos.get(sourceFile.path); + Debug.assert(!!info); + + const prevSignature = info.signature; + let latestSignature: string; + if (sourceFile.isDeclarationFile) { + latestSignature = sourceFile.version; + } + else { + const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); + if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { + latestSignature = computeHash(emitOutput.outputFiles[0].text); + } + else { + latestSignature = prevSignature; + } + } + cacheToUpdateSignature.set(sourceFile.path, latestSignature); + + return !prevSignature || latestSignature !== prevSignature; + } + + /** + * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true + */ + function getReferencedFiles(program: Program, sourceFile: SourceFile): Map | undefined { + let referencedFiles: Map | undefined; + + // We need to use a set here since the code can contain the same import twice, + // but that will only be one dependency. + // To avoid invernal conversion, the key of the referencedFiles map must be of type Path + if (sourceFile.imports && sourceFile.imports.length > 0) { + const checker: TypeChecker = program.getTypeChecker(); + for (const importName of sourceFile.imports) { + const symbol = checker.getSymbolAtLocation(importName); + if (symbol && symbol.declarations && symbol.declarations[0]) { + const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); + if (declarationSourceFile) { + addReferencedFile(declarationSourceFile.path); + } + } + } + } + + const sourceFileDirectory = getDirectoryPath(sourceFile.path); + // Handle triple slash references + if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { + for (const referencedFile of sourceFile.referencedFiles) { + const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); + addReferencedFile(referencedPath); + } + } + + // Handle type reference directives + if (sourceFile.resolvedTypeReferenceDirectiveNames) { + sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { + if (!resolvedTypeReferenceDirective) { + return; + } + + const fileName = resolvedTypeReferenceDirective.resolvedFileName; + const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); + addReferencedFile(typeFilePath); + }); + } + + return referencedFiles; + + function addReferencedFile(referencedPath: Path) { + if (!referencedFiles) { + referencedFiles = createMap(); + } + referencedFiles.set(referencedPath, true); + } + } + + /** + * Gets the files referenced by the the file path + */ + function getReferencedByPaths(referencedFilePath: Path) { + return mapDefinedIter(referencedMap.entries(), ([filePath, referencesInFile]) => + referencesInFile.has(referencedFilePath) ? filePath as Path : undefined + ); + } + + /** + * Gets all files of the program excluding the default library file + */ + function getAllFilesExcludingDefaultLibraryFile(program: Program, firstSourceFile: SourceFile): ReadonlyArray { + // Use cached result + if (allFilesExcludingDefaultLibraryFile) { + return allFilesExcludingDefaultLibraryFile; + } + + let result: SourceFile[]; + addSourceFile(firstSourceFile); + for (const sourceFile of program.getSourceFiles()) { + if (sourceFile !== firstSourceFile) { + addSourceFile(sourceFile); + } + } + allFilesExcludingDefaultLibraryFile = result || emptyArray; + return allFilesExcludingDefaultLibraryFile; + + function addSourceFile(sourceFile: SourceFile) { + if (!program.isSourceFileDefaultLibrary(sourceFile)) { + (result || (result = [])).push(sourceFile); + } + } + } + + /** + * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { + const compilerOptions = programOfThisState.getCompilerOptions(); + // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, + // so returning the file itself is good enough. + if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; + } + return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); + } + + /** + * When program emits modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { + if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { + return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); + } + + const compilerOptions = programOfThisState.getCompilerOptions(); + if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; + } + + // Now we need to if each file in the referencedBy list has a shape change as well. + // Because if so, its own referencedBy files need to be saved as well to make the + // emitting result consistent with files on disk. + const seenFileNamesMap = createMap(); + + // Start with the paths this file was referenced by + seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape); + const queue = getReferencedByPaths(sourceFileWithUpdatedShape.path); + while (queue.length > 0) { + const currentPath = queue.pop(); + if (!seenFileNamesMap.has(currentPath)) { + const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); + seenFileNamesMap.set(currentPath, currentSourceFile); + if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken)) { + queue.push(...getReferencedByPaths(currentPath)); + } + } + } + + // Return array of values that needs emit + return flatMapIter(seenFileNamesMap.values(), value => value); + } + } +} diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 9960b501d78..7a8911a5cbb 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,7 +1,6 @@ /// /// /// -/// namespace ts { const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; diff --git a/src/server/project.ts b/src/server/project.ts index 1fa12e50120..a5542cb89e7 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -3,7 +3,7 @@ /// /// /// -/// +/// namespace ts.server { @@ -139,7 +139,7 @@ namespace ts.server { /*@internal*/ resolutionCache: ResolutionCache; - private builder: InternalBuilder | undefined; + private builder: BuilderState | undefined; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -461,9 +461,13 @@ namespace ts.server { } this.updateGraph(); if (!this.builder) { - this.builder = createInternalBuilder({ - useCaseSensitiveFileNames: () => this.useCaseSensitiveFileNames(), - createHash: data => this.projectService.host.createHash(data) + this.builder = createBuilderState({ + useCaseSensitiveFileNames: this.useCaseSensitiveFileNames(), + createHash: data => this.projectService.host.createHash(data), + onUpdateProgramInitialized: noop, + onSourceFileAdd: noop, + onSourceFileChanged: noop, + onSourceFileRemoved: noop }); } this.builder.updateProgram(this.program); From 2586bb303c976eb8f55a47e95a9b450690f8b119 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 12:39:26 -0800 Subject: [PATCH 028/100] From builder use the builderState containing references and file infos --- src/compiler/builder.ts | 480 ++---------------- src/compiler/builderState.ts | 214 +++++--- .../reference/api/tsserverlibrary.d.ts | 92 +--- tests/baselines/reference/api/typescript.d.ts | 62 +-- 4 files changed, 214 insertions(+), 634 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 9c7707bd1d8..ff372479fad 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1,101 +1,26 @@ -/// +/// /*@internal*/ namespace ts { - export function getFileEmitOutput(program: Program, sourceFile: SourceFile, emitOnlyDtsFiles: boolean, - cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): EmitOutput { - const outputFiles: OutputFile[] = []; - const emitResult = program.emit(sourceFile, writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); - return { outputFiles, emitSkipped: emitResult.emitSkipped }; - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { - outputFiles.push({ name: fileName, writeByteOrderMark, text }); - } - } - - /** - * Internal Builder to get files affected by another file - */ - export interface InternalBuilder extends BaseBuilder { - /** - * Gets the files affected by the file path - * This api is only for internal use - */ - /*@internal*/ - getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken): ReadonlyArray; - } - - /** - * Create the internal builder to get files affected by sourceFile - */ - export function createInternalBuilder(host: BuilderHost): InternalBuilder { - return createBuilder(host, BuilderKind.BuilderKindInternal); - } - export enum BuilderKind { - BuilderKindInternal, BuilderKindSemanticDiagnostics, BuilderKindEmitAndSemanticDiagnostics } - /** - * Information about the source file: Its version and optional signature from last emit - */ - interface FileInfo { - version: string; - signature?: string; - } - - /** - * Referenced files with values for the keys as referenced file's path to be true - */ - type ReferencedSet = ReadonlyMap; - - function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { - if (map1 === undefined) { - return map2 === undefined; - } - if (map2 === undefined) { - return map1 === undefined; - } - // Has same size and every key is present in both maps - return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); - } - - export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindInternal): InternalBuilder; export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { /** - * Create the canonical file name for identity + * State corresponding to all the file references and shapes of the module etc */ - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); - /** - * Computing hash to for signature verification - */ - const computeHash = host.createHash || identity; - - /** - * Information of the file eg. its version, signature etc - */ - const fileInfos = createMap(); - - /** - * true if module emit is enabled - */ - let isModuleEmit: boolean; - - /** - * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled - * Otherwise undefined - */ - let referencedMap: Map | undefined; - - /** - * Get the files affected by the source file. - * This is dependent on whether its a module emit or not and hence function expression - */ - let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) => ReadonlyArray; + const state = createBuilderState({ + useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), + createHash: host.createHash, + onUpdateProgramInitialized, + onSourceFileAdd: addToChangedFilesSet, + onSourceFileChanged: path => { addToChangedFilesSet(path); deleteSemanticDiagnostics(path); }, + onSourceFileRemoved: deleteSemanticDiagnostics + }); /** * Cache of semantic diagnostics for files with their Path being the key @@ -107,18 +32,6 @@ namespace ts { */ const changedFilesSet = createMap(); - /** - * Map of files that have already called update signature. - * That means hence forth these files are assumed to have - * no change in their signature for this version of the program - */ - const hasCalledUpdateShapeSignature = createMap(); - - /** - * Cache of all files excluding default library file for the current program - */ - let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; - /** * Set of affected files being iterated */ @@ -142,8 +55,6 @@ namespace ts { const seenAffectedFiles = createMap(); switch (builderKind) { - case BuilderKind.BuilderKindInternal: - return getInternalBuilder(); case BuilderKind.BuilderKindSemanticDiagnostics: return getSemanticDiagnosticsBuilder(); case BuilderKind.BuilderKindEmitAndSemanticDiagnostics: @@ -152,14 +63,6 @@ namespace ts { notImplemented(); } - function getInternalBuilder(): InternalBuilder { - return { - updateProgram, - getFilesAffectedBy, - getAllDependencies - }; - } - function getSemanticDiagnosticsBuilder(): SemanticDiagnosticsBuilder { return { updateProgram, @@ -179,17 +82,13 @@ namespace ts { } /** - * Update current state to reflect new program - * Updates changed files, references, file infos etc + * Initialize changedFiles, affected files set, cached diagnostics, signatures */ - function updateProgram(newProgram: Program) { - const newProgramHasModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; - const oldReferencedMap = referencedMap; - if (isModuleEmit !== newProgramHasModuleEmit) { + function onUpdateProgramInitialized(isModuleEmitChanged: boolean) { + if (isModuleEmitChanged) { // Changes in the module emit, clear out everything and initialize as if first time // Clear file information and semantic diagnostics - fileInfos.clear(); semanticDiagnosticsPerFile.clear(); // Clear changed files and affected files information @@ -197,21 +96,12 @@ namespace ts { affectedFiles = undefined; currentChangedFilePath = undefined; currentAffectedFilesSignatures.clear(); - - // Update the reference map creation - referencedMap = newProgramHasModuleEmit ? createMap() : undefined; - - // Update the module emit - isModuleEmit = newProgramHasModuleEmit; - getEmitDependentFilesAffectedBy = isModuleEmit ? - getFilesAffectedByUpdatedShapeWhenModuleEmit : - getFilesAffectedByUpdatedShapeWhenNonModuleEmit; } else { if (currentChangedFilePath) { // Remove the diagnostics for all the affected files since we should resume the state such that // the whole iteration on currentChangedFile never happened - affectedFiles.map(sourceFile => semanticDiagnosticsPerFile.delete(sourceFile.path)); + affectedFiles.forEach(sourceFile => deleteSemanticDiagnostics(sourceFile.path)); affectedFiles = undefined; currentAffectedFilesSignatures.clear(); } @@ -219,100 +109,27 @@ namespace ts { // Verify the sanity of old state Debug.assert(!affectedFiles && !currentAffectedFilesSignatures.size, "Cannot reuse if only few affected files of currentChangedFile were iterated"); } - Debug.assert(!forEachEntry(changedFilesSet, (_value, path) => semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); - } - - // Clear datas that cant be retained beyond previous state - seenAffectedFiles.clear(); - hasCalledUpdateShapeSignature.clear(); - allFilesExcludingDefaultLibraryFile = undefined; - - // Create the reference map and update changed files - for (const sourceFile of newProgram.getSourceFiles()) { - const version = sourceFile.version; - const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); - const oldInfo = fileInfos.get(sourceFile.path); - let oldReferences: ReferencedSet; - - // Register changed file if its new file or we arent reusing old state - if (!oldInfo) { - // New file: Set the file info - fileInfos.set(sourceFile.path, { version }); - changedFilesSet.set(sourceFile.path, true); - } - // versions dont match - else if (oldInfo.version !== version || - // Referenced files changed - !hasSameKeys(newReferences, (oldReferences = oldReferencedMap && oldReferencedMap.get(sourceFile.path))) || - // Referenced file was deleted in the new program - newReferences && forEachEntry(newReferences, (_value, path) => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { - - // Changed file: Update the version, set as changed file - oldInfo.version = version; - changedFilesSet.set(sourceFile.path, true); - - // All changed files need to re-evaluate its semantic diagnostics - semanticDiagnosticsPerFile.delete(sourceFile.path); - } - - // Set the references - if (newReferences) { - referencedMap.set(sourceFile.path, newReferences); - } - else if (referencedMap) { - referencedMap.delete(sourceFile.path); - } - } - - // For removed files, remove the semantic diagnostics and file info - if (fileInfos.size > newProgram.getSourceFiles().length) { - fileInfos.forEach((_value, path) => { - if (!newProgram.getSourceFileByPath(path as Path)) { - fileInfos.delete(path); - semanticDiagnosticsPerFile.delete(path); - if (referencedMap) { - referencedMap.delete(path); - } - } - }); + Debug.assert(!forEachKey(changedFilesSet, path => semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); } } /** - * Gets the files affected by the path from the program + * Add file to the changed files set */ - function getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, cacheToUpdateSignature?: Map): ReadonlyArray { - // Since the operation could be cancelled, the signatures are always stored in the cache - // They will be commited once it is safe to use them - // eg when calling this api from tsserver, if there is no cancellation of the operation - // In the other cases the affected files signatures are commited only after the iteration through the result is complete - const signatureCache = cacheToUpdateSignature || createMap(); - const sourceFile = programOfThisState.getSourceFileByPath(path); - if (!sourceFile) { - return emptyArray; - } + function addToChangedFilesSet(path: Path) { + changedFilesSet.set(path, true); + } - if (!updateShapeSignature(programOfThisState, sourceFile, signatureCache, cancellationToken)) { - return [sourceFile]; - } - - const result = getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, signatureCache, cancellationToken); - if (!cacheToUpdateSignature) { - // Commit all the signatures in the signature cache - updateSignaturesFromCache(signatureCache); - } - return result; + function deleteSemanticDiagnostics(path: Path) { + semanticDiagnosticsPerFile.delete(path); } /** - * Updates the signatures from the cache - * This should be called whenever it is safe to commit the state of the builder + * Update current state to reflect new program + * Updates changed files, references, file infos etc which happens through the state callbacks */ - function updateSignaturesFromCache(signatureCache: Map) { - signatureCache.forEach((signature, path) => { - fileInfos.get(path).signature = signature; - hasCalledUpdateShapeSignature.set(path, true); - }); + function updateProgram(newProgram: Program) { + state.updateProgram(newProgram); } /** @@ -339,7 +156,7 @@ namespace ts { changedFilesSet.delete(currentChangedFilePath); currentChangedFilePath = undefined; // Commit the changes in file signature - updateSignaturesFromCache(currentAffectedFilesSignatures); + state.updateSignaturesFromCache(currentAffectedFilesSignatures); currentAffectedFilesSignatures.clear(); affectedFiles = undefined; } @@ -361,7 +178,7 @@ namespace ts { // Get next batch of affected files currentAffectedFilesSignatures.clear(); - affectedFiles = getFilesAffectedBy(programOfThisState, nextKey.value as Path, cancellationToken, currentAffectedFilesSignatures); + affectedFiles = state.getFilesAffectedBy(programOfThisState, nextKey.value as Path, cancellationToken, currentAffectedFilesSignatures); currentChangedFilePath = nextKey.value as Path; semanticDiagnosticsPerFile.delete(currentChangedFilePath); affectedFilesIndex = 0; @@ -500,250 +317,13 @@ namespace ts { /** * Get all the dependencies of the sourceFile */ - function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[] { - const compilerOptions = programOfThisState.getCompilerOptions(); - // With --out or --outFile all outputs go into single file, all files depend on each other - if (compilerOptions.outFile || compilerOptions.out) { - return programOfThisState.getSourceFiles().map(getFileName); - } - - // If this is non module emit, or its a global file, it depends on all the source files - if (!isModuleEmit || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { - return programOfThisState.getSourceFiles().map(getFileName); - } - - // Get the references, traversing deep from the referenceMap - Debug.assert(!!referencedMap); - const seenMap = createMap(); - const queue = [sourceFile.path]; - while (queue.length) { - const path = queue.pop(); - if (!seenMap.has(path)) { - seenMap.set(path, true); - const references = referencedMap.get(path); - if (references) { - const iterator = references.keys(); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - queue.push(value as Path); - } - } - } - } - - return flatMapIter(seenMap.keys(), path => { - const file = programOfThisState.getSourceFileByPath(path as Path); - if (file) { - return file.fileName; - } - return path; - }); - } - - function getFileName(sourceFile: SourceFile) { - return sourceFile.fileName; - } - - /** - * For script files that contains only ambient external modules, although they are not actually external module files, - * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, - * there are no point to rebuild all script files if these special files have changed. However, if any statement - * in the file is not ambient external module, we treat it as a regular script file. - */ - function containsOnlyAmbientModules(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if (!isModuleWithStringLiteralName(statement)) { - return false; - } - } - return true; - } - - /** - * Returns if the shape of the signature has changed since last emit - * Note that it also updates the current signature as the latest signature for the file - */ - function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { - Debug.assert(!!sourceFile); - - // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate - if (hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { - return false; - } - - const info = fileInfos.get(sourceFile.path); - Debug.assert(!!info); - - const prevSignature = info.signature; - let latestSignature: string; - if (sourceFile.isDeclarationFile) { - latestSignature = sourceFile.version; - } - else { - const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); - if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { - latestSignature = computeHash(emitOutput.outputFiles[0].text); - } - else { - latestSignature = prevSignature; - } - } - cacheToUpdateSignature.set(sourceFile.path, latestSignature); - - return !prevSignature || latestSignature !== prevSignature; - } - - /** - * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true - */ - function getReferencedFiles(program: Program, sourceFile: SourceFile): Map | undefined { - let referencedFiles: Map | undefined; - - // We need to use a set here since the code can contain the same import twice, - // but that will only be one dependency. - // To avoid invernal conversion, the key of the referencedFiles map must be of type Path - if (sourceFile.imports && sourceFile.imports.length > 0) { - const checker: TypeChecker = program.getTypeChecker(); - for (const importName of sourceFile.imports) { - const symbol = checker.getSymbolAtLocation(importName); - if (symbol && symbol.declarations && symbol.declarations[0]) { - const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); - if (declarationSourceFile) { - addReferencedFile(declarationSourceFile.path); - } - } - } - } - - const sourceFileDirectory = getDirectoryPath(sourceFile.path); - // Handle triple slash references - if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { - for (const referencedFile of sourceFile.referencedFiles) { - const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); - addReferencedFile(referencedPath); - } - } - - // Handle type reference directives - if (sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { - if (!resolvedTypeReferenceDirective) { - return; - } - - const fileName = resolvedTypeReferenceDirective.resolvedFileName; - const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); - addReferencedFile(typeFilePath); - }); - } - - return referencedFiles; - - function addReferencedFile(referencedPath: Path) { - if (!referencedFiles) { - referencedFiles = createMap(); - } - referencedFiles.set(referencedPath, true); - } - } - - /** - * Gets the files referenced by the the file path - */ - function getReferencedByPaths(referencedFilePath: Path) { - return mapDefinedIter(referencedMap.entries(), ([filePath, referencesInFile]) => - referencesInFile.has(referencedFilePath) ? filePath as Path : undefined - ); - } - - /** - * Gets all files of the program excluding the default library file - */ - function getAllFilesExcludingDefaultLibraryFile(program: Program, firstSourceFile: SourceFile): ReadonlyArray { - // Use cached result - if (allFilesExcludingDefaultLibraryFile) { - return allFilesExcludingDefaultLibraryFile; - } - - let result: SourceFile[]; - addSourceFile(firstSourceFile); - for (const sourceFile of program.getSourceFiles()) { - if (sourceFile !== firstSourceFile) { - addSourceFile(sourceFile); - } - } - allFilesExcludingDefaultLibraryFile = result || emptyArray; - return allFilesExcludingDefaultLibraryFile; - - function addSourceFile(sourceFile: SourceFile) { - if (!program.isSourceFileDefaultLibrary(sourceFile)) { - (result || (result = [])).push(sourceFile); - } - } - } - - /** - * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed - */ - function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { - const compilerOptions = programOfThisState.getCompilerOptions(); - // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, - // so returning the file itself is good enough. - if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) { - return [sourceFileWithUpdatedShape]; - } - return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); - } - - /** - * When program emits modular code, gets the files affected by the sourceFile whose shape has changed - */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { - if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { - return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); - } - - const compilerOptions = programOfThisState.getCompilerOptions(); - if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { - return [sourceFileWithUpdatedShape]; - } - - // Now we need to if each file in the referencedBy list has a shape change as well. - // Because if so, its own referencedBy files need to be saved as well to make the - // emitting result consistent with files on disk. - const seenFileNamesMap = createMap(); - - // Start with the paths this file was referenced by - seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape); - const queue = getReferencedByPaths(sourceFileWithUpdatedShape.path); - while (queue.length > 0) { - const currentPath = queue.pop(); - if (!seenFileNamesMap.has(currentPath)) { - const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); - seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken)) { - queue.push(...getReferencedByPaths(currentPath)); - } - } - } - - // Return array of values that needs emit - return flatMapIter(seenFileNamesMap.values(), value => value); + function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile) { + return state.getAllDependencies(programOfThisState, sourceFile); } } } namespace ts { - export interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } - - export interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } - export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; export interface BuilderHost { @@ -769,7 +349,7 @@ namespace ts { /** * Get all the dependencies of the file */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; } /** diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index f6706195a75..eb3887eed15 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -46,7 +46,76 @@ namespace ts { return map1 === undefined; } // Has same size and every key is present in both maps - return map1.size === map2.size && !forEachEntry(map1, (_value, key) => !map2.has(key)); + return map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); + } + + /** + * For script files that contains only ambient external modules, although they are not actually external module files, + * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, + * there are no point to rebuild all script files if these special files have changed. However, if any statement + * in the file is not ambient external module, we treat it as a regular script file. + */ + function containsOnlyAmbientModules(sourceFile: SourceFile) { + for (const statement of sourceFile.statements) { + if (!isModuleWithStringLiteralName(statement)) { + return false; + } + } + return true; + } + + /** + * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true + */ + function getReferencedFiles(program: Program, sourceFile: SourceFile, getCanonicalFileName: GetCanonicalFileName): Map | undefined { + let referencedFiles: Map | undefined; + + // We need to use a set here since the code can contain the same import twice, + // but that will only be one dependency. + // To avoid invernal conversion, the key of the referencedFiles map must be of type Path + if (sourceFile.imports && sourceFile.imports.length > 0) { + const checker: TypeChecker = program.getTypeChecker(); + for (const importName of sourceFile.imports) { + const symbol = checker.getSymbolAtLocation(importName); + if (symbol && symbol.declarations && symbol.declarations[0]) { + const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); + if (declarationSourceFile) { + addReferencedFile(declarationSourceFile.path); + } + } + } + } + + const sourceFileDirectory = getDirectoryPath(sourceFile.path); + // Handle triple slash references + if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { + for (const referencedFile of sourceFile.referencedFiles) { + const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); + addReferencedFile(referencedPath); + } + } + + // Handle type reference directives + if (sourceFile.resolvedTypeReferenceDirectiveNames) { + sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { + if (!resolvedTypeReferenceDirective) { + return; + } + + const fileName = resolvedTypeReferenceDirective.resolvedFileName; + const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); + addReferencedFile(typeFilePath); + }); + } + + return referencedFiles; + + function addReferencedFile(referencedPath: Path) { + if (!referencedFiles) { + referencedFiles = createMap(); + } + referencedFiles.set(referencedPath, true); + } } export interface BuilderStateHost { @@ -76,6 +145,15 @@ namespace ts { * Gets the files affected by the file path */ getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken, cacheToUpdateSignature?: Map): ReadonlyArray; + /** + * Updates the signatures from the cache + * This should be called whenever it is safe to commit the state of the builder + */ + updateSignaturesFromCache(signatureCache: Map): void; + /** + * Get all the dependencies of the sourceFile + */ + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; } export function createBuilderState(host: BuilderStateHost): BuilderState { @@ -121,11 +199,17 @@ namespace ts { * Cache of all files excluding default library file for the current program */ let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; + /** + * Cache of all the file names + */ + let allFileNames: ReadonlyArray | undefined; return { updateProgram, getFilesAffectedBy, - }; + getAllDependencies, + updateSignaturesFromCache + }; /** * Update current state to reflect new program @@ -155,11 +239,12 @@ namespace ts { // Clear datas that cant be retained beyond previous state hasCalledUpdateShapeSignature.clear(); allFilesExcludingDefaultLibraryFile = undefined; + allFileNames = undefined; // Create the reference map and update changed files for (const sourceFile of newProgram.getSourceFiles()) { const version = sourceFile.version; - const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile); + const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile, getCanonicalFileName); const oldInfo = fileInfos.get(sourceFile.path); let oldReferences: ReferencedSet; @@ -174,7 +259,7 @@ namespace ts { // Referenced files changed !hasSameKeys(newReferences, (oldReferences = oldReferencedMap && oldReferencedMap.get(sourceFile.path))) || // Referenced file was deleted in the new program - newReferences && forEachEntry(newReferences, (_value, path) => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { + newReferences && forEachKey(newReferences, path => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { // Changed file: Update the version, set as changed file oldInfo.version = version; @@ -230,6 +315,58 @@ namespace ts { return result; } + /** + * Get all the dependencies of the sourceFile + */ + function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray { + const compilerOptions = programOfThisState.getCompilerOptions(); + // With --out or --outFile all outputs go into single file, all files depend on each other + if (compilerOptions.outFile || compilerOptions.out) { + return getAllFileNames(programOfThisState); + } + + // If this is non module emit, or its a global file, it depends on all the source files + if (!isModuleEmit || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { + return getAllFileNames(programOfThisState); + } + + // Get the references, traversing deep from the referenceMap + Debug.assert(!!referencedMap); + const seenMap = createMap(); + const queue = [sourceFile.path]; + while (queue.length) { + const path = queue.pop(); + if (!seenMap.has(path)) { + seenMap.set(path, true); + const references = referencedMap.get(path); + if (references) { + const iterator = references.keys(); + for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { + queue.push(value as Path); + } + } + } + } + + return flatMapIter(seenMap.keys(), path => { + const file = programOfThisState.getSourceFileByPath(path as Path); + if (file) { + return file.fileName; + } + return path; + }); + } + + /** + * Gets the names of all files from the program + */ + function getAllFileNames(programOfThisState: Program): ReadonlyArray { + if (!allFileNames) { + allFileNames = programOfThisState.getSourceFiles().map(file => file.fileName); + } + return allFileNames; + } + /** * Updates the signatures from the cache * This should be called whenever it is safe to commit the state of the builder @@ -241,21 +378,6 @@ namespace ts { }); } - /** - * For script files that contains only ambient external modules, although they are not actually external module files, - * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, - * there are no point to rebuild all script files if these special files have changed. However, if any statement - * in the file is not ambient external module, we treat it as a regular script file. - */ - function containsOnlyAmbientModules(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if (!isModuleWithStringLiteralName(statement)) { - return false; - } - } - return true; - } - /** * Returns if the shape of the signature has changed since last emit * Note that it also updates the current signature as the latest signature for the file @@ -290,60 +412,6 @@ namespace ts { return !prevSignature || latestSignature !== prevSignature; } - /** - * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true - */ - function getReferencedFiles(program: Program, sourceFile: SourceFile): Map | undefined { - let referencedFiles: Map | undefined; - - // We need to use a set here since the code can contain the same import twice, - // but that will only be one dependency. - // To avoid invernal conversion, the key of the referencedFiles map must be of type Path - if (sourceFile.imports && sourceFile.imports.length > 0) { - const checker: TypeChecker = program.getTypeChecker(); - for (const importName of sourceFile.imports) { - const symbol = checker.getSymbolAtLocation(importName); - if (symbol && symbol.declarations && symbol.declarations[0]) { - const declarationSourceFile = getSourceFileOfNode(symbol.declarations[0]); - if (declarationSourceFile) { - addReferencedFile(declarationSourceFile.path); - } - } - } - } - - const sourceFileDirectory = getDirectoryPath(sourceFile.path); - // Handle triple slash references - if (sourceFile.referencedFiles && sourceFile.referencedFiles.length > 0) { - for (const referencedFile of sourceFile.referencedFiles) { - const referencedPath = toPath(referencedFile.fileName, sourceFileDirectory, getCanonicalFileName); - addReferencedFile(referencedPath); - } - } - - // Handle type reference directives - if (sourceFile.resolvedTypeReferenceDirectiveNames) { - sourceFile.resolvedTypeReferenceDirectiveNames.forEach((resolvedTypeReferenceDirective) => { - if (!resolvedTypeReferenceDirective) { - return; - } - - const fileName = resolvedTypeReferenceDirective.resolvedFileName; - const typeFilePath = toPath(fileName, sourceFileDirectory, getCanonicalFileName); - addReferencedFile(typeFilePath); - }); - } - - return referencedFiles; - - function addReferencedFile(referencedPath: Path) { - if (!referencedFiles) { - referencedFiles = createMap(); - } - referencedFiles.set(referencedPath, true); - } - } - /** * Gets the files referenced by the the file path */ diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index d7f3821a25d..32f4b78a8c5 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -3757,87 +3757,6 @@ declare namespace ts { declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } -declare namespace ts { - interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; - } - interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; - } - type AffectedFileResult = { - result: T; - affected: SourceFile | Program; - } | undefined; - interface BuilderHost { - /** - * return true if file names are treated with case sensitivity - */ - useCaseSensitiveFileNames(): boolean; - /** - * If provided this would be used this hash instead of actual file shape text for detecting changes - */ - createHash?: (data: string) => string; - } - /** - * Builder to manage the program state changes - */ - interface BaseBuilder { - /** - * Updates the program in the builder to represent new state - */ - updateProgram(newProgram: Program): void; - /** - * Get all the dependencies of the file - */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; - } - /** - * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files - */ - interface SemanticDiagnosticsBuilder extends BaseBuilder { - /** - * Gets the semantic diagnostics from the program for the next affected file and caches it - * Returns undefined if the iteration is complete - */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; - } - /** - * The builder that can handle the changes in program and iterate through changed file to emit the files - * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files - */ - interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { - /** - * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete - */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; - } - /** - * Create the builder to manage semantic diagnostics and cache them - */ - function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder; - /** - * Create the builder that can handle the changes in program and iterate through changed files - * to emit the those files and manage semantic diagnostics cache as well - */ - function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; -} declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; function resolveTripleslashReference(moduleName: string, containingFile: string): string; @@ -7233,6 +7152,17 @@ declare namespace ts.server { onProjectClosed(project: Project): void; } } +declare namespace ts { + interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } +} declare namespace ts.server { enum ProjectKind { Inferred = 0, diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 06c75e843c0..514c140f05e 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3704,6 +3704,35 @@ declare namespace ts { declare namespace ts { function createPrinter(printerOptions?: PrinterOptions, handlers?: PrintHandlers): Printer; } +declare namespace ts { + function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; + function resolveTripleslashReference(moduleName: string, containingFile: string): string; + function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; + function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; + interface FormatDiagnosticsHost { + getCurrentDirectory(): string; + getCanonicalFileName(fileName: string): string; + getNewLine(): string; + } + function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; + function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; + function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; + function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; + /** + * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' + * that represent a compilation unit. + * + * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and + * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in. + * + * @param rootNames - A set of root files. + * @param options - The compiler options which should be used. + * @param host - The host interacts with the underlying file system. + * @param oldProgram - Reuses an old program structure. + * @returns A 'Program' object. + */ + function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; +} declare namespace ts { interface EmitOutput { outputFiles: OutputFile[]; @@ -3714,6 +3743,8 @@ declare namespace ts { writeByteOrderMark: boolean; text: string; } +} +declare namespace ts { type AffectedFileResult = { result: T; affected: SourceFile | Program; @@ -3739,7 +3770,7 @@ declare namespace ts { /** * Get all the dependencies of the file */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): string[]; + getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; } /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files @@ -3785,35 +3816,6 @@ declare namespace ts { */ function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; } -declare namespace ts { - function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string; - function resolveTripleslashReference(moduleName: string, containingFile: string): string; - function createCompilerHost(options: CompilerOptions, setParentNodes?: boolean): CompilerHost; - function getPreEmitDiagnostics(program: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): Diagnostic[]; - interface FormatDiagnosticsHost { - getCurrentDirectory(): string; - getCanonicalFileName(fileName: string): string; - getNewLine(): string; - } - function formatDiagnostics(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; - function formatDiagnostic(diagnostic: Diagnostic, host: FormatDiagnosticsHost): string; - function formatDiagnosticsWithColorAndContext(diagnostics: ReadonlyArray, host: FormatDiagnosticsHost): string; - function flattenDiagnosticMessageText(messageText: string | DiagnosticMessageChain, newLine: string): string; - /** - * Create a new 'Program' instance. A Program is an immutable collection of 'SourceFile's and a 'CompilerOptions' - * that represent a compilation unit. - * - * Creating a program proceeds from a set of root files, expanding the set of inputs by following imports and - * triple-slash-reference-path directives transitively. '@types' and triple-slash-reference-types are also pulled in. - * - * @param rootNames - A set of root files. - * @param options - The compiler options which should be used. - * @param host - The host interacts with the underlying file system. - * @param oldProgram - Reuses an old program structure. - * @returns A 'Program' object. - */ - function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; -} declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; /** From bb0fc0d2bcd88d99c148fbff650d6746b3bc7413 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 14:04:40 -0800 Subject: [PATCH 029/100] Convert builder state to mutable data, so that later we can create builder Program out of this --- src/compiler/builder.ts | 2 +- src/compiler/builderState.ts | 249 +++++++++++++++++++++++++++++++++-- src/server/project.ts | 20 +-- 3 files changed, 243 insertions(+), 28 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index ff372479fad..198bf0556f0 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -13,7 +13,7 @@ namespace ts { /** * State corresponding to all the file references and shapes of the module etc */ - const state = createBuilderState({ + const state = createBuilderStateOld({ useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), createHash: host.createHash, onUpdateProgramInitialized, diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index eb3887eed15..0cd55ebd61f 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -136,7 +136,7 @@ namespace ts { onSourceFileRemoved(path: Path): void; } - export interface BuilderState { + export interface BuilderStateOld { /** * Updates the program in the builder to represent new state */ @@ -156,7 +156,65 @@ namespace ts { getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; } - export function createBuilderState(host: BuilderStateHost): BuilderState { + export interface BuilderState { + /** + * Information of the file eg. its version, signature etc + */ + fileInfos: Map; + /** + * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled + * Otherwise undefined + * Thus non undefined value indicates, module emit + */ + readonly referencedMap: ReadonlyMap | undefined; + /** + * Map of files that have already called update signature. + * That means hence forth these files are assumed to have + * no change in their signature for this version of the program + */ + hasCalledUpdateShapeSignature: Map; + /** + * Cache of all files excluding default library file for the current program + */ + allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; + /** + * Cache of all the file names + */ + allFileNames: ReadonlyArray | undefined; + } + + export function createBuilderState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: BuilderState): BuilderState { + const fileInfos = createMap(); + const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap() : undefined; + const hasCalledUpdateShapeSignature = createMap(); + const useOldState = oldState && !!oldState.referencedMap !== !!referencedMap; + + // Create the reference map, and set the file infos + for (const sourceFile of newProgram.getSourceFiles()) { + const version = sourceFile.version; + const oldInfo = useOldState && oldState.fileInfos.get(sourceFile.path); + if (referencedMap) { + const newReferences = getReferencedFiles(newProgram, sourceFile, getCanonicalFileName); + if (newReferences) { + referencedMap.set(sourceFile.path, newReferences); + } + } + fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); + } + + oldState = undefined; + newProgram = undefined; + + return { + fileInfos, + referencedMap, + hasCalledUpdateShapeSignature, + allFilesExcludingDefaultLibraryFile: undefined, + allFileNames: undefined + }; + } + + export function createBuilderStateOld(host: BuilderStateHost): BuilderStateOld { /** * Create the canonical file name for identity */ @@ -171,11 +229,6 @@ namespace ts { */ const fileInfos = createMap(); - /** - * true if module emit is enabled - */ - let isModuleEmit: boolean; - /** * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled * Otherwise undefined @@ -218,7 +271,7 @@ namespace ts { function updateProgram(newProgram: Program) { const newProgramHasModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; const oldReferencedMap = referencedMap; - const isModuleEmitChanged = isModuleEmit !== newProgramHasModuleEmit; + const isModuleEmitChanged = !!referencedMap !== newProgramHasModuleEmit; if (isModuleEmitChanged) { // Changes in the module emit, clear out everything and initialize as if first time @@ -229,8 +282,7 @@ namespace ts { referencedMap = newProgramHasModuleEmit ? createMap() : undefined; // Update the module emit - isModuleEmit = newProgramHasModuleEmit; - getEmitDependentFilesAffectedBy = isModuleEmit ? + getEmitDependentFilesAffectedBy = newProgramHasModuleEmit ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit; } @@ -326,12 +378,11 @@ namespace ts { } // If this is non module emit, or its a global file, it depends on all the source files - if (!isModuleEmit || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { + if (!referencedMap || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { return getAllFileNames(programOfThisState); } // Get the references, traversing deep from the referenceMap - Debug.assert(!!referencedMap); const seenMap = createMap(); const queue = [sourceFile.path]; while (queue.length) { @@ -497,3 +548,177 @@ namespace ts { } } } + +/*@internal*/ +namespace ts.BuilderState { + type ComputeHash = (data: string) => string; + + /** + * Gets the files affected by the path from the program + */ + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash?: ComputeHash, cacheToUpdateSignature?: Map): ReadonlyArray { + // Since the operation could be cancelled, the signatures are always stored in the cache + // They will be commited once it is safe to use them + // eg when calling this api from tsserver, if there is no cancellation of the operation + // In the other cases the affected files signatures are commited only after the iteration through the result is complete + const signatureCache = cacheToUpdateSignature || createMap(); + const sourceFile = programOfThisState.getSourceFileByPath(path); + if (!sourceFile) { + return emptyArray; + } + + if (!updateShapeSignature(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash)) { + return [sourceFile]; + } + + const result = (state.referencedMap ? getFilesAffectedByUpdatedShapeWhenModuleEmit : getFilesAffectedByUpdatedShapeWhenNonModuleEmit)(state, programOfThisState, sourceFile, signatureCache, cancellationToken, computeHash); + if (!cacheToUpdateSignature) { + // Commit all the signatures in the signature cache + updateSignaturesFromCache(state, signatureCache); + } + return result; + } + + /** + * Updates the signatures from the cache into state's fileinfo signatures + * This should be called whenever it is safe to commit the state of the builder + */ + export function updateSignaturesFromCache(state: BuilderState, signatureCache: Map) { + signatureCache.forEach((signature, path) => { + state.fileInfos.get(path).signature = signature; + state.hasCalledUpdateShapeSignature.set(path, true); + }); + } + + /** + * Returns if the shape of the signature has changed since last emit + */ + function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined) { + Debug.assert(!!sourceFile); + + // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate + if (state.hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { + return false; + } + + const info = state.fileInfos.get(sourceFile.path); + Debug.assert(!!info); + + const prevSignature = info.signature; + let latestSignature: string; + if (sourceFile.isDeclarationFile) { + latestSignature = sourceFile.version; + } + else { + const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); + if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { + latestSignature = (computeHash || identity)(emitOutput.outputFiles[0].text); + } + else { + latestSignature = prevSignature; + } + } + cacheToUpdateSignature.set(sourceFile.path, latestSignature); + + return !prevSignature || latestSignature !== prevSignature; + } + + /** + * Gets the files referenced by the the file path + */ + function getReferencedByPaths(state: Readonly, referencedFilePath: Path) { + return mapDefinedIter(state.referencedMap.entries(), ([filePath, referencesInFile]) => + referencesInFile.has(referencedFilePath) ? filePath as Path : undefined + ); + } + + /** + * For script files that contains only ambient external modules, although they are not actually external module files, + * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, + * there are no point to rebuild all script files if these special files have changed. However, if any statement + * in the file is not ambient external module, we treat it as a regular script file. + */ + function containsOnlyAmbientModules(sourceFile: SourceFile) { + for (const statement of sourceFile.statements) { + if (!isModuleWithStringLiteralName(statement)) { + return false; + } + } + return true; + } + + /** + * Gets all files of the program excluding the default library file + */ + function getAllFilesExcludingDefaultLibraryFile(state: BuilderState, programOfThisState: Program, firstSourceFile: SourceFile): ReadonlyArray { + // Use cached result + if (state.allFilesExcludingDefaultLibraryFile) { + return state.allFilesExcludingDefaultLibraryFile; + } + + let result: SourceFile[]; + addSourceFile(firstSourceFile); + for (const sourceFile of programOfThisState.getSourceFiles()) { + if (sourceFile !== firstSourceFile) { + addSourceFile(sourceFile); + } + } + state.allFilesExcludingDefaultLibraryFile = result || emptyArray; + return state.allFilesExcludingDefaultLibraryFile; + + function addSourceFile(sourceFile: SourceFile) { + if (!programOfThisState.isSourceFileDefaultLibrary(sourceFile)) { + (result || (result = [])).push(sourceFile); + } + } + } + + /** + * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { + const compilerOptions = programOfThisState.getCompilerOptions(); + // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, + // so returning the file itself is good enough. + if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; + } + return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); + } + + /** + * When program emits modular code, gets the files affected by the sourceFile whose shape has changed + */ + function getFilesAffectedByUpdatedShapeWhenModuleEmit(state: BuilderState, programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined) { + if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { + return getAllFilesExcludingDefaultLibraryFile(state, programOfThisState, sourceFileWithUpdatedShape); + } + + const compilerOptions = programOfThisState.getCompilerOptions(); + if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { + return [sourceFileWithUpdatedShape]; + } + + // Now we need to if each file in the referencedBy list has a shape change as well. + // Because if so, its own referencedBy files need to be saved as well to make the + // emitting result consistent with files on disk. + const seenFileNamesMap = createMap(); + + // Start with the paths this file was referenced by + seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape); + const queue = getReferencedByPaths(state, sourceFileWithUpdatedShape.path); + while (queue.length > 0) { + const currentPath = queue.pop(); + if (!seenFileNamesMap.has(currentPath)) { + const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); + seenFileNamesMap.set(currentPath, currentSourceFile); + if (currentSourceFile && updateShapeSignature(state, programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken, computeHash)) { + queue.push(...getReferencedByPaths(state, currentPath)); + } + } + } + + // Return array of values that needs emit + return flatMapIter(seenFileNamesMap.values(), value => value); + } +} diff --git a/src/server/project.ts b/src/server/project.ts index a5542cb89e7..95c41eaaf44 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -139,7 +139,7 @@ namespace ts.server { /*@internal*/ resolutionCache: ResolutionCache; - private builder: BuilderState | undefined; + private builderState: BuilderState | undefined; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -460,18 +460,8 @@ namespace ts.server { return []; } this.updateGraph(); - if (!this.builder) { - this.builder = createBuilderState({ - useCaseSensitiveFileNames: this.useCaseSensitiveFileNames(), - createHash: data => this.projectService.host.createHash(data), - onUpdateProgramInitialized: noop, - onSourceFileAdd: noop, - onSourceFileChanged: noop, - onSourceFileRemoved: noop - }); - } - this.builder.updateProgram(this.program); - return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path, this.cancellationToken), + this.builderState = createBuilderState(this.program, this.projectService.toCanonicalFileName, this.builderState); + return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash(data)), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined); } @@ -507,7 +497,7 @@ namespace ts.server { } this.languageService.cleanupSemanticCache(); this.languageServiceEnabled = false; - this.builder = undefined; + this.builderState = undefined; this.resolutionCache.closeTypeRootsWatch(); this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false); } @@ -548,7 +538,7 @@ namespace ts.server { this.rootFilesMap = undefined; this.externalFiles = undefined; this.program = undefined; - this.builder = undefined; + this.builderState = undefined; this.resolutionCache.clear(); this.resolutionCache = undefined; this.cachedUnresolvedImportsPerFile = undefined; From 965f40f2132a9aa09c6647eecc6f12b0800e7aaa Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 16:46:36 -0800 Subject: [PATCH 030/100] Use builder state in the semantic/emit builder as well --- src/compiler/builder.ts | 204 ++++--- src/compiler/builderState.ts | 540 ++++-------------- src/server/project.ts | 2 +- .../reference/api/tsserverlibrary.d.ts | 2 +- 4 files changed, 219 insertions(+), 529 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 198bf0556f0..110bdd5130a 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -7,52 +7,118 @@ namespace ts { BuilderKindEmitAndSemanticDiagnostics } - export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; - export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; - export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { - /** - * State corresponding to all the file references and shapes of the module etc - */ - const state = createBuilderStateOld({ - useCaseSensitiveFileNames: host.useCaseSensitiveFileNames(), - createHash: host.createHash, - onUpdateProgramInitialized, - onSourceFileAdd: addToChangedFilesSet, - onSourceFileChanged: path => { addToChangedFilesSet(path); deleteSemanticDiagnostics(path); }, - onSourceFileRemoved: deleteSemanticDiagnostics - }); - + interface BuilderStateWithChangedFiles extends BuilderState { /** * Cache of semantic diagnostics for files with their Path being the key */ - const semanticDiagnosticsPerFile = createMap>(); - + semanticDiagnosticsPerFile: Map> | undefined; /** * The map has key by source file's path that has been changed */ - const changedFilesSet = createMap(); - + changedFilesSet: Map; /** * Set of affected files being iterated */ - let affectedFiles: ReadonlyArray | undefined; + affectedFiles: ReadonlyArray | undefined; /** * Current index to retrieve affected file from */ - let affectedFilesIndex = 0; + affectedFilesIndex: number | undefined; /** * Current changed file for iterating over affected files */ - let currentChangedFilePath: Path | undefined; + currentChangedFilePath: Path | undefined; /** * Map of file signatures, with key being file path, calculated while getting current changed file's affected files * These will be commited whenever the iteration through affected files of current changed file is complete */ - const currentAffectedFilesSignatures = createMap(); + currentAffectedFilesSignatures: Map | undefined; /** * Already seen affected files */ - const seenAffectedFiles = createMap(); + seenAffectedFiles: Map | undefined; + } + + function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { + if (map1 === undefined) { + return map2 === undefined; + } + if (map2 === undefined) { + return map1 === undefined; + } + // Has same size and every key is present in both maps + return map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); + } + + /** + * Create the state so that we can iterate on changedFiles/affected files + */ + function createBuilderStateWithChangedFiles(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderStateWithChangedFiles { + const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderStateWithChangedFiles; + const compilerOptions = newProgram.getCompilerOptions(); + if (!compilerOptions.outFile && !compilerOptions.out) { + state.semanticDiagnosticsPerFile = createMap>(); + } + state.changedFilesSet = createMap(); + const useOldState = BuilderState.canReuseOldState(state.referencedMap, oldState); + const canCopySemanticDiagnostics = useOldState && oldState.semanticDiagnosticsPerFile && !!state.semanticDiagnosticsPerFile; + if (useOldState) { + // Verify the sanity of old state + if (!oldState.currentChangedFilePath) { + Debug.assert(!oldState.affectedFiles && (!oldState.currentAffectedFilesSignatures || !oldState.currentAffectedFilesSignatures.size), "Cannot reuse if only few affected files of currentChangedFile were iterated"); + } + if (canCopySemanticDiagnostics) { + Debug.assert(!forEachKey(oldState.changedFilesSet, path => oldState.semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); + } + + // Copy old state's changed files set + copyEntries(oldState.changedFilesSet, state.changedFilesSet); + } + + // Update changed files and copy semantic diagnostics if we can + const referencedMap = state.referencedMap; + const oldReferencedMap = useOldState && oldState.referencedMap; + state.fileInfos.forEach((info, sourceFilePath) => { + let oldInfo: Readonly; + let newReferences: BuilderState.ReferencedSet; + + // if not using old state, every file is changed + if (!useOldState || + // File wasnt present in old state + !(oldInfo = oldState.fileInfos.get(sourceFilePath)) || + // versions dont match + oldInfo.version !== info.version || + // Referenced files changed + !hasSameKeys(newReferences = referencedMap && referencedMap.get(sourceFilePath), oldReferencedMap && oldReferencedMap.get(sourceFilePath)) || + // Referenced file was deleted in the new program + newReferences && forEachKey(newReferences, path => !state.fileInfos.has(path) && oldState.fileInfos.has(path))) { + // Register file as changed file and do not copy semantic diagnostics, since all changed files need to be re-evaluated + state.changedFilesSet.set(sourceFilePath, true); + } + else if (canCopySemanticDiagnostics) { + // Unchanged file copy diagnostics + const diagnostics = oldState.semanticDiagnosticsPerFile.get(sourceFilePath); + if (diagnostics) { + state.semanticDiagnosticsPerFile.set(sourceFilePath, diagnostics); + } + } + }); + + return state; + } + + export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; + export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; + export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { + /** + * Create the canonical file name for identity + */ + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); + /** + * Computing hash to for signature verification + */ + const computeHash = host.createHash || identity; + let state: BuilderStateWithChangedFiles; switch (builderKind) { case BuilderKind.BuilderKindSemanticDiagnostics: @@ -81,55 +147,12 @@ namespace ts { }; } - /** - * Initialize changedFiles, affected files set, cached diagnostics, signatures - */ - function onUpdateProgramInitialized(isModuleEmitChanged: boolean) { - if (isModuleEmitChanged) { - // Changes in the module emit, clear out everything and initialize as if first time - - // Clear file information and semantic diagnostics - semanticDiagnosticsPerFile.clear(); - - // Clear changed files and affected files information - changedFilesSet.clear(); - affectedFiles = undefined; - currentChangedFilePath = undefined; - currentAffectedFilesSignatures.clear(); - } - else { - if (currentChangedFilePath) { - // Remove the diagnostics for all the affected files since we should resume the state such that - // the whole iteration on currentChangedFile never happened - affectedFiles.forEach(sourceFile => deleteSemanticDiagnostics(sourceFile.path)); - affectedFiles = undefined; - currentAffectedFilesSignatures.clear(); - } - else { - // Verify the sanity of old state - Debug.assert(!affectedFiles && !currentAffectedFilesSignatures.size, "Cannot reuse if only few affected files of currentChangedFile were iterated"); - } - Debug.assert(!forEachKey(changedFilesSet, path => semanticDiagnosticsPerFile.has(path)), "Semantic diagnostics shouldnt be available for changed files"); - } - } - - /** - * Add file to the changed files set - */ - function addToChangedFilesSet(path: Path) { - changedFilesSet.set(path, true); - } - - function deleteSemanticDiagnostics(path: Path) { - semanticDiagnosticsPerFile.delete(path); - } - /** * Update current state to reflect new program * Updates changed files, references, file infos etc which happens through the state callbacks */ function updateProgram(newProgram: Program) { - state.updateProgram(newProgram); + state = createBuilderStateWithChangedFiles(newProgram, getCanonicalFileName, state); } /** @@ -140,11 +163,15 @@ namespace ts { */ function getNextAffectedFile(programOfThisState: Program, cancellationToken: CancellationToken | undefined): SourceFile | Program | undefined { while (true) { + const { affectedFiles } = state; if (affectedFiles) { + const { seenAffectedFiles, semanticDiagnosticsPerFile } = state; + let { affectedFilesIndex } = state; while (affectedFilesIndex < affectedFiles.length) { const affectedFile = affectedFiles[affectedFilesIndex]; if (!seenAffectedFiles.has(affectedFile.path)) { // Set the next affected file as seen and remove the cached semantic diagnostics + state.affectedFilesIndex = affectedFilesIndex; semanticDiagnosticsPerFile.delete(affectedFile.path); return affectedFile; } @@ -153,16 +180,16 @@ namespace ts { } // Remove the changed file from the change set - changedFilesSet.delete(currentChangedFilePath); - currentChangedFilePath = undefined; + state.changedFilesSet.delete(state.currentChangedFilePath); + state.currentChangedFilePath = undefined; // Commit the changes in file signature - state.updateSignaturesFromCache(currentAffectedFilesSignatures); - currentAffectedFilesSignatures.clear(); - affectedFiles = undefined; + BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures); + state.currentAffectedFilesSignatures.clear(); + state.affectedFiles = undefined; } // Get next changed file - const nextKey = changedFilesSet.keys().next(); + const nextKey = state.changedFilesSet.keys().next(); if (nextKey.done) { // Done return undefined; @@ -172,16 +199,17 @@ namespace ts { // With --out or --outFile all outputs go into single file // so operations are performed directly on program, return program if (compilerOptions.outFile || compilerOptions.out) { - Debug.assert(semanticDiagnosticsPerFile.size === 0); + Debug.assert(!state.semanticDiagnosticsPerFile); return programOfThisState; } // Get next batch of affected files - currentAffectedFilesSignatures.clear(); - affectedFiles = state.getFilesAffectedBy(programOfThisState, nextKey.value as Path, cancellationToken, currentAffectedFilesSignatures); - currentChangedFilePath = nextKey.value as Path; - semanticDiagnosticsPerFile.delete(currentChangedFilePath); - affectedFilesIndex = 0; + state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap(); + state.affectedFiles = BuilderState.getFilesAffectedBy(state, programOfThisState, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures); + state.currentChangedFilePath = nextKey.value as Path; + state.semanticDiagnosticsPerFile.delete(nextKey.value as Path); + state.affectedFilesIndex = 0; + state.seenAffectedFiles = state.seenAffectedFiles || createMap(); } } @@ -191,11 +219,11 @@ namespace ts { */ function doneWithAffectedFile(programOfThisState: Program, affected: SourceFile | Program) { if (affected === programOfThisState) { - changedFilesSet.clear(); + state.changedFilesSet.clear(); } else { - seenAffectedFiles.set((affected).path, true); - affectedFilesIndex++; + state.seenAffectedFiles.set((affected as SourceFile).path, true); + state.affectedFilesIndex++; } } @@ -277,10 +305,10 @@ namespace ts { * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files */ function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { - Debug.assert(!affectedFiles || affectedFiles[affectedFilesIndex - 1] !== sourceFile || !semanticDiagnosticsPerFile.has(sourceFile.path)); + Debug.assert(!state.affectedFiles || state.affectedFiles[state.affectedFilesIndex - 1] !== sourceFile || !state.semanticDiagnosticsPerFile.has(sourceFile.path)); const compilerOptions = programOfThisState.getCompilerOptions(); if (compilerOptions.outFile || compilerOptions.out) { - Debug.assert(semanticDiagnosticsPerFile.size === 0); + Debug.assert(!state.semanticDiagnosticsPerFile); // We dont need to cache the diagnostics just return them from program return programOfThisState.getSemanticDiagnostics(sourceFile, cancellationToken); } @@ -302,7 +330,7 @@ namespace ts { */ function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { const path = sourceFile.path; - const cachedDiagnostics = semanticDiagnosticsPerFile.get(path); + const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path); // Report the semantic diagnostics from the cache if we already have those diagnostics present if (cachedDiagnostics) { return cachedDiagnostics; @@ -310,7 +338,7 @@ namespace ts { // Diagnostics werent cached, get them from program, and cache the result const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); - semanticDiagnosticsPerFile.set(path, diagnostics); + state.semanticDiagnosticsPerFile.set(path, diagnostics); return diagnostics; } @@ -318,7 +346,7 @@ namespace ts { * Get all the dependencies of the sourceFile */ function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile) { - return state.getAllDependencies(programOfThisState, sourceFile); + return BuilderState.getAllDependencies(state, programOfThisState, sourceFile); } } } diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 0cd55ebd61f..79ba3767b93 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -25,44 +25,51 @@ namespace ts { } } + export interface BuilderState { + /** + * Information of the file eg. its version, signature etc + */ + fileInfos: Map; + /** + * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled + * Otherwise undefined + * Thus non undefined value indicates, module emit + */ + readonly referencedMap: ReadonlyMap | undefined; + /** + * Map of files that have already called update signature. + * That means hence forth these files are assumed to have + * no change in their signature for this version of the program + */ + hasCalledUpdateShapeSignature: Map; + /** + * Cache of all files excluding default library file for the current program + */ + allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; + /** + * Cache of all the file names + */ + allFileNames: ReadonlyArray | undefined; + } +} + +/*@internal*/ +namespace ts.BuilderState { /** * Information about the source file: Its version and optional signature from last emit */ - interface FileInfo { - version: string; - signature?: string; + export interface FileInfo { + readonly version: string; + signature: string | undefined; } - /** * Referenced files with values for the keys as referenced file's path to be true */ - type ReferencedSet = ReadonlyMap; - - function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { - if (map1 === undefined) { - return map2 === undefined; - } - if (map2 === undefined) { - return map1 === undefined; - } - // Has same size and every key is present in both maps - return map1.size === map2.size && !forEachKey(map1, key => !map2.has(key)); - } - + export type ReferencedSet = ReadonlyMap; /** - * For script files that contains only ambient external modules, although they are not actually external module files, - * they can only be consumed via importing elements from them. Regular script files cannot consume them. Therefore, - * there are no point to rebuild all script files if these special files have changed. However, if any statement - * in the file is not ambient external module, we treat it as a regular script file. + * Compute the hash to store the shape of the file */ - function containsOnlyAmbientModules(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if (!isModuleWithStringLiteralName(statement)) { - return false; - } - } - return true; - } + export type ComputeHash = (data: string) => string; /** * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true @@ -118,76 +125,21 @@ namespace ts { } } - export interface BuilderStateHost { - /** - * true if file names are treated with case sensitivity - */ - useCaseSensitiveFileNames: boolean; - /** - * if provided this would be used this hash instead of actual file shape text for detecting changes - */ - createHash?: (data: string) => string; - /** - * Called when programState is initialized, indicating if isModuleEmit is changed - */ - onUpdateProgramInitialized(isModuleEmitChanged: boolean): void; - onSourceFileAdd(path: Path): void; - onSourceFileChanged(path: Path): void; - onSourceFileRemoved(path: Path): void; + /** + * Returns true if oldState is reusable, that is the emitKind = module/non module has not changed + */ + export function canReuseOldState(newReferencedMap: ReadonlyMap, oldState: Readonly | undefined) { + return oldState && !oldState.referencedMap === !newReferencedMap; } - export interface BuilderStateOld { - /** - * Updates the program in the builder to represent new state - */ - updateProgram(newProgram: Program): void; - /** - * Gets the files affected by the file path - */ - getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken, cacheToUpdateSignature?: Map): ReadonlyArray; - /** - * Updates the signatures from the cache - * This should be called whenever it is safe to commit the state of the builder - */ - updateSignaturesFromCache(signatureCache: Map): void; - /** - * Get all the dependencies of the sourceFile - */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; - } - - export interface BuilderState { - /** - * Information of the file eg. its version, signature etc - */ - fileInfos: Map; - /** - * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled - * Otherwise undefined - * Thus non undefined value indicates, module emit - */ - readonly referencedMap: ReadonlyMap | undefined; - /** - * Map of files that have already called update signature. - * That means hence forth these files are assumed to have - * no change in their signature for this version of the program - */ - hasCalledUpdateShapeSignature: Map; - /** - * Cache of all files excluding default library file for the current program - */ - allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; - /** - * Cache of all the file names - */ - allFileNames: ReadonlyArray | undefined; - } - - export function createBuilderState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: BuilderState): BuilderState { + /** + * Creates the state of file references and signature for the new program from oldState if it is safe + */ + export function create(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderState { const fileInfos = createMap(); const referencedMap = newProgram.getCompilerOptions().module !== ModuleKind.None ? createMap() : undefined; const hasCalledUpdateShapeSignature = createMap(); - const useOldState = oldState && !!oldState.referencedMap !== !!referencedMap; + const useOldState = canReuseOldState(referencedMap, oldState); // Create the reference map, and set the file infos for (const sourceFile of newProgram.getSourceFiles()) { @@ -202,9 +154,6 @@ namespace ts { fileInfos.set(sourceFile.path, { version, signature: oldInfo && oldInfo.signature }); } - oldState = undefined; - newProgram = undefined; - return { fileInfos, referencedMap, @@ -214,349 +163,10 @@ namespace ts { }; } - export function createBuilderStateOld(host: BuilderStateHost): BuilderStateOld { - /** - * Create the canonical file name for identity - */ - const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames); - /** - * Computing hash to for signature verification - */ - const computeHash = host.createHash || identity; - - /** - * Information of the file eg. its version, signature etc - */ - const fileInfos = createMap(); - - /** - * Contains the map of ReferencedSet=Referenced files of the file if module emit is enabled - * Otherwise undefined - */ - let referencedMap: Map | undefined; - - /** - * Get the files affected by the source file. - * This is dependent on whether its a module emit or not and hence function expression - */ - let getEmitDependentFilesAffectedBy: (programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) => ReadonlyArray; - - /** - * Map of files that have already called update signature. - * That means hence forth these files are assumed to have - * no change in their signature for this version of the program - */ - const hasCalledUpdateShapeSignature = createMap(); - - /** - * Cache of all files excluding default library file for the current program - */ - let allFilesExcludingDefaultLibraryFile: ReadonlyArray | undefined; - /** - * Cache of all the file names - */ - let allFileNames: ReadonlyArray | undefined; - - return { - updateProgram, - getFilesAffectedBy, - getAllDependencies, - updateSignaturesFromCache - }; - - /** - * Update current state to reflect new program - * Updates changed files, references, file infos etc - */ - function updateProgram(newProgram: Program) { - const newProgramHasModuleEmit = newProgram.getCompilerOptions().module !== ModuleKind.None; - const oldReferencedMap = referencedMap; - const isModuleEmitChanged = !!referencedMap !== newProgramHasModuleEmit; - if (isModuleEmitChanged) { - // Changes in the module emit, clear out everything and initialize as if first time - - // Clear file information - fileInfos.clear(); - - // Update the reference map creation - referencedMap = newProgramHasModuleEmit ? createMap() : undefined; - - // Update the module emit - getEmitDependentFilesAffectedBy = newProgramHasModuleEmit ? - getFilesAffectedByUpdatedShapeWhenModuleEmit : - getFilesAffectedByUpdatedShapeWhenNonModuleEmit; - } - host.onUpdateProgramInitialized(isModuleEmitChanged); - - // Clear datas that cant be retained beyond previous state - hasCalledUpdateShapeSignature.clear(); - allFilesExcludingDefaultLibraryFile = undefined; - allFileNames = undefined; - - // Create the reference map and update changed files - for (const sourceFile of newProgram.getSourceFiles()) { - const version = sourceFile.version; - const newReferences = referencedMap && getReferencedFiles(newProgram, sourceFile, getCanonicalFileName); - const oldInfo = fileInfos.get(sourceFile.path); - let oldReferences: ReferencedSet; - - // Register changed file if its new file or we arent reusing old state - if (!oldInfo) { - // New file: Set the file info - fileInfos.set(sourceFile.path, { version }); - host.onSourceFileAdd(sourceFile.path); - } - // versions dont match - else if (oldInfo.version !== version || - // Referenced files changed - !hasSameKeys(newReferences, (oldReferences = oldReferencedMap && oldReferencedMap.get(sourceFile.path))) || - // Referenced file was deleted in the new program - newReferences && forEachKey(newReferences, path => !newProgram.getSourceFileByPath(path as Path) && fileInfos.has(path))) { - - // Changed file: Update the version, set as changed file - oldInfo.version = version; - host.onSourceFileChanged(sourceFile.path); - } - - // Set the references - if (newReferences) { - referencedMap.set(sourceFile.path, newReferences); - } - else if (referencedMap) { - referencedMap.delete(sourceFile.path); - } - } - - // For removed files, remove the semantic diagnostics and file info - if (fileInfos.size > newProgram.getSourceFiles().length) { - fileInfos.forEach((_value, path) => { - if (!newProgram.getSourceFileByPath(path as Path)) { - fileInfos.delete(path); - host.onSourceFileRemoved(path as Path); - if (referencedMap) { - referencedMap.delete(path); - } - } - }); - } - } - - /** - * Gets the files affected by the path from the program - */ - function getFilesAffectedBy(programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, cacheToUpdateSignature?: Map): ReadonlyArray { - // Since the operation could be cancelled, the signatures are always stored in the cache - // They will be commited once it is safe to use them - // eg when calling this api from tsserver, if there is no cancellation of the operation - // In the other cases the affected files signatures are commited only after the iteration through the result is complete - const signatureCache = cacheToUpdateSignature || createMap(); - const sourceFile = programOfThisState.getSourceFileByPath(path); - if (!sourceFile) { - return emptyArray; - } - - if (!updateShapeSignature(programOfThisState, sourceFile, signatureCache, cancellationToken)) { - return [sourceFile]; - } - - const result = getEmitDependentFilesAffectedBy(programOfThisState, sourceFile, signatureCache, cancellationToken); - if (!cacheToUpdateSignature) { - // Commit all the signatures in the signature cache - updateSignaturesFromCache(signatureCache); - } - return result; - } - - /** - * Get all the dependencies of the sourceFile - */ - function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray { - const compilerOptions = programOfThisState.getCompilerOptions(); - // With --out or --outFile all outputs go into single file, all files depend on each other - if (compilerOptions.outFile || compilerOptions.out) { - return getAllFileNames(programOfThisState); - } - - // If this is non module emit, or its a global file, it depends on all the source files - if (!referencedMap || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { - return getAllFileNames(programOfThisState); - } - - // Get the references, traversing deep from the referenceMap - const seenMap = createMap(); - const queue = [sourceFile.path]; - while (queue.length) { - const path = queue.pop(); - if (!seenMap.has(path)) { - seenMap.set(path, true); - const references = referencedMap.get(path); - if (references) { - const iterator = references.keys(); - for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { - queue.push(value as Path); - } - } - } - } - - return flatMapIter(seenMap.keys(), path => { - const file = programOfThisState.getSourceFileByPath(path as Path); - if (file) { - return file.fileName; - } - return path; - }); - } - - /** - * Gets the names of all files from the program - */ - function getAllFileNames(programOfThisState: Program): ReadonlyArray { - if (!allFileNames) { - allFileNames = programOfThisState.getSourceFiles().map(file => file.fileName); - } - return allFileNames; - } - - /** - * Updates the signatures from the cache - * This should be called whenever it is safe to commit the state of the builder - */ - function updateSignaturesFromCache(signatureCache: Map) { - signatureCache.forEach((signature, path) => { - fileInfos.get(path).signature = signature; - hasCalledUpdateShapeSignature.set(path, true); - }); - } - - /** - * Returns if the shape of the signature has changed since last emit - * Note that it also updates the current signature as the latest signature for the file - */ - function updateShapeSignature(program: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { - Debug.assert(!!sourceFile); - - // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate - if (hasCalledUpdateShapeSignature.has(sourceFile.path) || cacheToUpdateSignature.has(sourceFile.path)) { - return false; - } - - const info = fileInfos.get(sourceFile.path); - Debug.assert(!!info); - - const prevSignature = info.signature; - let latestSignature: string; - if (sourceFile.isDeclarationFile) { - latestSignature = sourceFile.version; - } - else { - const emitOutput = getFileEmitOutput(program, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); - if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { - latestSignature = computeHash(emitOutput.outputFiles[0].text); - } - else { - latestSignature = prevSignature; - } - } - cacheToUpdateSignature.set(sourceFile.path, latestSignature); - - return !prevSignature || latestSignature !== prevSignature; - } - - /** - * Gets the files referenced by the the file path - */ - function getReferencedByPaths(referencedFilePath: Path) { - return mapDefinedIter(referencedMap.entries(), ([filePath, referencesInFile]) => - referencesInFile.has(referencedFilePath) ? filePath as Path : undefined - ); - } - - /** - * Gets all files of the program excluding the default library file - */ - function getAllFilesExcludingDefaultLibraryFile(program: Program, firstSourceFile: SourceFile): ReadonlyArray { - // Use cached result - if (allFilesExcludingDefaultLibraryFile) { - return allFilesExcludingDefaultLibraryFile; - } - - let result: SourceFile[]; - addSourceFile(firstSourceFile); - for (const sourceFile of program.getSourceFiles()) { - if (sourceFile !== firstSourceFile) { - addSourceFile(sourceFile); - } - } - allFilesExcludingDefaultLibraryFile = result || emptyArray; - return allFilesExcludingDefaultLibraryFile; - - function addSourceFile(sourceFile: SourceFile) { - if (!program.isSourceFileDefaultLibrary(sourceFile)) { - (result || (result = [])).push(sourceFile); - } - } - } - - /** - * When program emits non modular code, gets the files affected by the sourceFile whose shape has changed - */ - function getFilesAffectedByUpdatedShapeWhenNonModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile) { - const compilerOptions = programOfThisState.getCompilerOptions(); - // If `--out` or `--outFile` is specified, any new emit will result in re-emitting the entire project, - // so returning the file itself is good enough. - if (compilerOptions && (compilerOptions.out || compilerOptions.outFile)) { - return [sourceFileWithUpdatedShape]; - } - return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); - } - - /** - * When program emits modular code, gets the files affected by the sourceFile whose shape has changed - */ - function getFilesAffectedByUpdatedShapeWhenModuleEmit(programOfThisState: Program, sourceFileWithUpdatedShape: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined) { - if (!isExternalModule(sourceFileWithUpdatedShape) && !containsOnlyAmbientModules(sourceFileWithUpdatedShape)) { - return getAllFilesExcludingDefaultLibraryFile(programOfThisState, sourceFileWithUpdatedShape); - } - - const compilerOptions = programOfThisState.getCompilerOptions(); - if (compilerOptions && (compilerOptions.isolatedModules || compilerOptions.out || compilerOptions.outFile)) { - return [sourceFileWithUpdatedShape]; - } - - // Now we need to if each file in the referencedBy list has a shape change as well. - // Because if so, its own referencedBy files need to be saved as well to make the - // emitting result consistent with files on disk. - const seenFileNamesMap = createMap(); - - // Start with the paths this file was referenced by - seenFileNamesMap.set(sourceFileWithUpdatedShape.path, sourceFileWithUpdatedShape); - const queue = getReferencedByPaths(sourceFileWithUpdatedShape.path); - while (queue.length > 0) { - const currentPath = queue.pop(); - if (!seenFileNamesMap.has(currentPath)) { - const currentSourceFile = programOfThisState.getSourceFileByPath(currentPath); - seenFileNamesMap.set(currentPath, currentSourceFile); - if (currentSourceFile && updateShapeSignature(programOfThisState, currentSourceFile, cacheToUpdateSignature, cancellationToken)) { - queue.push(...getReferencedByPaths(currentPath)); - } - } - } - - // Return array of values that needs emit - return flatMapIter(seenFileNamesMap.values(), value => value); - } - } -} - -/*@internal*/ -namespace ts.BuilderState { - type ComputeHash = (data: string) => string; - /** * Gets the files affected by the path from the program */ - export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash?: ComputeHash, cacheToUpdateSignature?: Map): ReadonlyArray { + export function getFilesAffectedBy(state: BuilderState, programOfThisState: Program, path: Path, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash, cacheToUpdateSignature?: Map): ReadonlyArray { // Since the operation could be cancelled, the signatures are always stored in the cache // They will be commited once it is safe to use them // eg when calling this api from tsserver, if there is no cancellation of the operation @@ -593,7 +203,7 @@ namespace ts.BuilderState { /** * Returns if the shape of the signature has changed since last emit */ - function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash | undefined) { + function updateShapeSignature(state: Readonly, programOfThisState: Program, sourceFile: SourceFile, cacheToUpdateSignature: Map, cancellationToken: CancellationToken | undefined, computeHash: ComputeHash) { Debug.assert(!!sourceFile); // If we have cached the result for this file, that means hence forth we should assume file shape is uptodate @@ -612,7 +222,7 @@ namespace ts.BuilderState { else { const emitOutput = getFileEmitOutput(programOfThisState, sourceFile, /*emitOnlyDtsFiles*/ true, cancellationToken); if (emitOutput.outputFiles && emitOutput.outputFiles.length > 0) { - latestSignature = (computeHash || identity)(emitOutput.outputFiles[0].text); + latestSignature = computeHash(emitOutput.outputFiles[0].text); } else { latestSignature = prevSignature; @@ -623,6 +233,58 @@ namespace ts.BuilderState { return !prevSignature || latestSignature !== prevSignature; } + /** + * Get all the dependencies of the sourceFile + */ + export function getAllDependencies(state: BuilderState, programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray { + const compilerOptions = programOfThisState.getCompilerOptions(); + // With --out or --outFile all outputs go into single file, all files depend on each other + if (compilerOptions.outFile || compilerOptions.out) { + return getAllFileNames(state, programOfThisState); + } + + // If this is non module emit, or its a global file, it depends on all the source files + if (!state.referencedMap || (!isExternalModule(sourceFile) && !containsOnlyAmbientModules(sourceFile))) { + return getAllFileNames(state, programOfThisState); + } + + // Get the references, traversing deep from the referenceMap + const seenMap = createMap(); + const queue = [sourceFile.path]; + while (queue.length) { + const path = queue.pop(); + if (!seenMap.has(path)) { + seenMap.set(path, true); + const references = state.referencedMap.get(path); + if (references) { + const iterator = references.keys(); + for (let { value, done } = iterator.next(); !done; { value, done } = iterator.next()) { + queue.push(value as Path); + } + } + } + } + + return flatMapIter(seenMap.keys(), path => { + const file = programOfThisState.getSourceFileByPath(path as Path); + if (file) { + return file.fileName; + } + return path; + }); + } + + /** + * Gets the names of all files from the program + */ + function getAllFileNames(state: BuilderState, programOfThisState: Program): ReadonlyArray { + if (!state.allFileNames) { + const sourceFiles = programOfThisState.getSourceFiles(); + state.allFileNames = sourceFiles === emptyArray ? emptyArray : sourceFiles.map(file => file.fileName); + } + return state.allFileNames; + } + /** * Gets the files referenced by the the file path */ diff --git a/src/server/project.ts b/src/server/project.ts index 95c41eaaf44..b4e15774cfb 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -460,7 +460,7 @@ namespace ts.server { return []; } this.updateGraph(); - this.builderState = createBuilderState(this.program, this.projectService.toCanonicalFileName, this.builderState); + this.builderState = BuilderState.create(this.program, this.projectService.toCanonicalFileName, this.builderState); return mapDefined(BuilderState.getFilesAffectedBy(this.builderState, this.program, scriptInfo.path, this.cancellationToken, data => this.projectService.host.createHash(data)), sourceFile => this.shouldEmitFile(this.projectService.getScriptInfoForPath(sourceFile.path)) ? sourceFile.fileName : undefined); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 32f4b78a8c5..575c1dc07ce 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -7217,7 +7217,7 @@ declare namespace ts.server { languageServiceEnabled: boolean; readonly trace?: (s: string) => void; readonly realpath?: (path: string) => string; - private builder; + private builderState; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ From dc62bb9abc28aed804b055db3c8e45b24eda336b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 18:55:11 -0800 Subject: [PATCH 031/100] Change builder to BuilderProgram so it is similar to operating on program --- src/compiler/builder.ts | 519 ++++++++++-------- src/compiler/tsconfig.json | 1 + src/compiler/watch.ts | 163 ++---- src/harness/unittests/builder.ts | 14 +- src/services/tsconfig.json | 1 + tests/baselines/reference/api/typescript.d.ts | 100 ++-- 6 files changed, 417 insertions(+), 381 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 110bdd5130a..21c3b9021ee 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -2,12 +2,10 @@ /*@internal*/ namespace ts { - export enum BuilderKind { - BuilderKindSemanticDiagnostics, - BuilderKindEmitAndSemanticDiagnostics - } - - interface BuilderStateWithChangedFiles extends BuilderState { + /** + * State to store the changed files, affected files and cache semantic diagnostics + */ + export interface BuilderProgramState extends BuilderState { /** * Cache of semantic diagnostics for files with their Path being the key */ @@ -37,6 +35,10 @@ namespace ts { * Already seen affected files */ seenAffectedFiles: Map | undefined; + /** + * program corresponding to this state + */ + program: Program; } function hasSameKeys(map1: ReadonlyMap | undefined, map2: ReadonlyMap | undefined) { @@ -53,8 +55,9 @@ namespace ts { /** * Create the state so that we can iterate on changedFiles/affected files */ - function createBuilderStateWithChangedFiles(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderStateWithChangedFiles { - const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderStateWithChangedFiles; + function createBuilderProgramState(newProgram: Program, getCanonicalFileName: GetCanonicalFileName, oldState?: Readonly): BuilderProgramState { + const state = BuilderState.create(newProgram, getCanonicalFileName, oldState) as BuilderProgramState; + state.program = newProgram; const compilerOptions = newProgram.getCompilerOptions(); if (!compilerOptions.outFile && !compilerOptions.out) { state.semanticDiagnosticsPerFile = createMap>(); @@ -107,9 +110,119 @@ namespace ts { return state; } - export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindSemanticDiagnostics): SemanticDiagnosticsBuilder; - export function createBuilder(host: BuilderHost, builderKind: BuilderKind.BuilderKindEmitAndSemanticDiagnostics): EmitAndSemanticDiagnosticsBuilder; - export function createBuilder(host: BuilderHost, builderKind: BuilderKind) { + /** + * Verifies that source file is ok to be used in calls that arent handled by next + */ + function assertSourceFileOkWithoutNextAffectedCall(state: BuilderProgramState, sourceFile: SourceFile | undefined) { + Debug.assert(!sourceFile || !state.affectedFiles || state.affectedFiles[state.affectedFilesIndex - 1] !== sourceFile || !state.semanticDiagnosticsPerFile.has(sourceFile.path)); + } + + /** + * This function returns the next affected file to be processed. + * Note that until doneAffected is called it would keep reporting same result + * This is to allow the callers to be able to actually remove affected file only when the operation is complete + * eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained + */ + function getNextAffectedFile(state: BuilderProgramState, cancellationToken: CancellationToken | undefined, computeHash: BuilderState.ComputeHash): SourceFile | Program | undefined { + while (true) { + const { affectedFiles } = state; + if (affectedFiles) { + const { seenAffectedFiles, semanticDiagnosticsPerFile } = state; + let { affectedFilesIndex } = state; + while (affectedFilesIndex < affectedFiles.length) { + const affectedFile = affectedFiles[affectedFilesIndex]; + if (!seenAffectedFiles.has(affectedFile.path)) { + // Set the next affected file as seen and remove the cached semantic diagnostics + state.affectedFilesIndex = affectedFilesIndex; + semanticDiagnosticsPerFile.delete(affectedFile.path); + return affectedFile; + } + seenAffectedFiles.set(affectedFile.path, true); + affectedFilesIndex++; + } + + // Remove the changed file from the change set + state.changedFilesSet.delete(state.currentChangedFilePath); + state.currentChangedFilePath = undefined; + // Commit the changes in file signature + BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures); + state.currentAffectedFilesSignatures.clear(); + state.affectedFiles = undefined; + } + + // Get next changed file + const nextKey = state.changedFilesSet.keys().next(); + if (nextKey.done) { + // Done + return undefined; + } + + // With --out or --outFile all outputs go into single file + // so operations are performed directly on program, return program + const compilerOptions = state.program.getCompilerOptions(); + if (compilerOptions.outFile || compilerOptions.out) { + Debug.assert(!state.semanticDiagnosticsPerFile); + return state.program; + } + + // Get next batch of affected files + state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap(); + state.affectedFiles = BuilderState.getFilesAffectedBy(state, state.program, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures); + state.currentChangedFilePath = nextKey.value as Path; + state.semanticDiagnosticsPerFile.delete(nextKey.value as Path); + state.affectedFilesIndex = 0; + state.seenAffectedFiles = state.seenAffectedFiles || createMap(); + } + } + + /** + * This is called after completing operation on the next affected file. + * The operations here are postponed to ensure that cancellation during the iteration is handled correctly + */ + function doneWithAffectedFile(state: BuilderProgramState, affected: SourceFile | Program) { + if (affected === state.program) { + state.changedFilesSet.clear(); + } + else { + state.seenAffectedFiles.set((affected as SourceFile).path, true); + state.affectedFilesIndex++; + } + } + + /** + * Returns the result with affected file + */ + function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult { + doneWithAffectedFile(state, affected); + return { result, affected }; + } + + /** + * Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it + * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set + */ + function getSemanticDiagnosticsOfFile(state: BuilderProgramState, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { + const path = sourceFile.path; + const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path); + // Report the semantic diagnostics from the cache if we already have those diagnostics present + if (cachedDiagnostics) { + return cachedDiagnostics; + } + + // Diagnostics werent cached, get them from program, and cache the result + const diagnostics = state.program.getSemanticDiagnostics(sourceFile, cancellationToken); + state.semanticDiagnosticsPerFile.set(path, diagnostics); + return diagnostics; + } + + export enum BuilderProgramKind { + SemanticDiagnosticsBuilderProgram, + EmitAndSemanticDiagnosticsBuilderProgram + } + + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind) { /** * Create the canonical file name for identity */ @@ -118,183 +231,129 @@ namespace ts { * Computing hash to for signature verification */ const computeHash = host.createHash || identity; - let state: BuilderStateWithChangedFiles; + const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldProgram && oldProgram.getState()); - switch (builderKind) { - case BuilderKind.BuilderKindSemanticDiagnostics: - return getSemanticDiagnosticsBuilder(); - case BuilderKind.BuilderKindEmitAndSemanticDiagnostics: - return getEmitAndSemanticDiagnosticsBuilder(); - default: - notImplemented(); + // To ensure that we arent storing any references to old program or new program without state + newProgram = undefined; + oldProgram = undefined; + + const result: BaseBuilderProgram = { + getState: () => state, + getCompilerOptions: () => state.program.getCompilerOptions(), + getSourceFile: fileName => state.program.getSourceFile(fileName), + getSourceFiles: () => state.program.getSourceFiles(), + getOptionsDiagnostics: cancellationToken => state.program.getOptionsDiagnostics(cancellationToken), + getGlobalDiagnostics: cancellationToken => state.program.getGlobalDiagnostics(cancellationToken), + getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken), + getSemanticDiagnostics, + emit, + getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile) + }; + + if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) { + (result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile; + } + else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { + (result as EmitAndSemanticDiagnosticsBuilderProgram).getCurrentDirectory = () => state.program.getCurrentDirectory(); + (result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile; + } + else { + notImplemented(); } - function getSemanticDiagnosticsBuilder(): SemanticDiagnosticsBuilder { - return { - updateProgram, - getAllDependencies, - getSemanticDiagnosticsOfNextAffectedFile, - getSemanticDiagnostics - }; - } - - function getEmitAndSemanticDiagnosticsBuilder(): EmitAndSemanticDiagnosticsBuilder { - return { - updateProgram, - getAllDependencies, - emitNextAffectedFile, - getSemanticDiagnostics - }; - } + return result; /** - * Update current state to reflect new program - * Updates changed files, references, file infos etc which happens through the state callbacks + * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files */ - function updateProgram(newProgram: Program) { - state = createBuilderStateWithChangedFiles(newProgram, getCanonicalFileName, state); - } - - /** - * This function returns the next affected file to be processed. - * Note that until doneAffected is called it would keep reporting same result - * This is to allow the callers to be able to actually remove affected file only when the operation is complete - * eg. if during diagnostics check cancellation token ends up cancelling the request, the affected file should be retained - */ - function getNextAffectedFile(programOfThisState: Program, cancellationToken: CancellationToken | undefined): SourceFile | Program | undefined { - while (true) { - const { affectedFiles } = state; - if (affectedFiles) { - const { seenAffectedFiles, semanticDiagnosticsPerFile } = state; - let { affectedFilesIndex } = state; - while (affectedFilesIndex < affectedFiles.length) { - const affectedFile = affectedFiles[affectedFilesIndex]; - if (!seenAffectedFiles.has(affectedFile.path)) { - // Set the next affected file as seen and remove the cached semantic diagnostics - state.affectedFilesIndex = affectedFilesIndex; - semanticDiagnosticsPerFile.delete(affectedFile.path); - return affectedFile; - } - seenAffectedFiles.set(affectedFile.path, true); - affectedFilesIndex++; - } - - // Remove the changed file from the change set - state.changedFilesSet.delete(state.currentChangedFilePath); - state.currentChangedFilePath = undefined; - // Commit the changes in file signature - BuilderState.updateSignaturesFromCache(state, state.currentAffectedFilesSignatures); - state.currentAffectedFilesSignatures.clear(); - state.affectedFiles = undefined; - } - - // Get next changed file - const nextKey = state.changedFilesSet.keys().next(); - if (nextKey.done) { - // Done - return undefined; - } - - const compilerOptions = programOfThisState.getCompilerOptions(); - // With --out or --outFile all outputs go into single file - // so operations are performed directly on program, return program - if (compilerOptions.outFile || compilerOptions.out) { - Debug.assert(!state.semanticDiagnosticsPerFile); - return programOfThisState; - } - - // Get next batch of affected files - state.currentAffectedFilesSignatures = state.currentAffectedFilesSignatures || createMap(); - state.affectedFiles = BuilderState.getFilesAffectedBy(state, programOfThisState, nextKey.value as Path, cancellationToken, computeHash, state.currentAffectedFilesSignatures); - state.currentChangedFilePath = nextKey.value as Path; - state.semanticDiagnosticsPerFile.delete(nextKey.value as Path); - state.affectedFilesIndex = 0; - state.seenAffectedFiles = state.seenAffectedFiles || createMap(); - } - } - - /** - * This is called after completing operation on the next affected file. - * The operations here are postponed to ensure that cancellation during the iteration is handled correctly - */ - function doneWithAffectedFile(programOfThisState: Program, affected: SourceFile | Program) { - if (affected === programOfThisState) { - state.changedFilesSet.clear(); - } - else { - state.seenAffectedFiles.set((affected as SourceFile).path, true); - state.affectedFilesIndex++; - } - } - - /** - * Returns the result with affected file - */ - function toAffectedFileResult(programOfThisState: Program, result: T, affected: SourceFile | Program): AffectedFileResult { - doneWithAffectedFile(programOfThisState, affected); - return { result, affected }; - } - - /** - * Emits the next affected file, and returns the EmitResult along with source files emitted - * Returns undefined when iteration is complete - */ - function emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult { - const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken); - if (!affectedFile) { + function emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult { + const affected = getNextAffectedFile(state, cancellationToken, computeHash); + if (!affected) { // Done return undefined; } - else if (affectedFile === programOfThisState) { - // When whole program is affected, do emit only once (eg when --out or --outFile is specified) - return toAffectedFileResult( - programOfThisState, - programOfThisState.emit(/*targetSourceFile*/ undefined, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), - programOfThisState - ); - } - // Emit the affected file - const targetSourceFile = affectedFile as SourceFile; return toAffectedFileResult( - programOfThisState, - programOfThisState.emit(targetSourceFile, writeFileCallback, cancellationToken, /*emitOnlyDtsFiles*/ false, customTransformers), - targetSourceFile + state, + // When whole program is affected, do emit only once (eg when --out or --outFile is specified) + // Otherwise just affected file + state.program.emit(affected === state.program ? undefined : affected as SourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers), + affected ); } + /** + * Emits the JavaScript and declaration files. + * When targetSource file is specified, emits the files corresponding to that source file, + * otherwise for the whole program. + * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified, + * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified, + * it will only emit all the affected files instead of whole program + * + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files + */ + function emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult { + if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { + assertSourceFileOkWithoutNextAffectedCall(state, targetSourceFile); + if (!targetSourceFile) { + // Emit and report any errors we ran into. + let sourceMaps: SourceMapData[] = []; + let emitSkipped: boolean; + let diagnostics: Diagnostic[]; + let emittedFiles: string[] = []; + + let affectedEmitResult: AffectedFileResult; + while (affectedEmitResult = emitNextAffectedFile(writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers)) { + emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; + diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); + emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); + sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); + } + return { + emitSkipped, + diagnostics: diagnostics || emptyArray, + emittedFiles, + sourceMaps + }; + } + } + return state.program.emit(targetSourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); + } + /** * Return the semantic diagnostics for the next affected file or undefined if iteration is complete * If provided ignoreSourceFile would be called before getting the diagnostics and would ignore the sourceFile if the returned value was true */ - function getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult> { + function getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult> { while (true) { - const affectedFile = getNextAffectedFile(programOfThisState, cancellationToken); - if (!affectedFile) { + const affected = getNextAffectedFile(state, cancellationToken, computeHash); + if (!affected) { // Done return undefined; } - else if (affectedFile === programOfThisState) { + else if (affected === state.program) { // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) return toAffectedFileResult( - programOfThisState, - programOfThisState.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), - programOfThisState + state, + state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), + affected ); } // Get diagnostics for the affected file if its not ignored - const targetSourceFile = affectedFile as SourceFile; - if (ignoreSourceFile && ignoreSourceFile(targetSourceFile)) { + if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) { // Get next affected file - doneWithAffectedFile(programOfThisState, targetSourceFile); + doneWithAffectedFile(state, affected); continue; } return toAffectedFileResult( - programOfThisState, - getSemanticDiagnosticsOfFile(programOfThisState, targetSourceFile, cancellationToken), - targetSourceFile + state, + getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken), + affected ); } } @@ -302,59 +361,46 @@ namespace ts { /** * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files + * Note that it is assumed that when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics + * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided, + * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics */ - function getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { - Debug.assert(!state.affectedFiles || state.affectedFiles[state.affectedFilesIndex - 1] !== sourceFile || !state.semanticDiagnosticsPerFile.has(sourceFile.path)); - const compilerOptions = programOfThisState.getCompilerOptions(); + function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { + assertSourceFileOkWithoutNextAffectedCall(state, sourceFile); + const compilerOptions = state.program.getCompilerOptions(); if (compilerOptions.outFile || compilerOptions.out) { Debug.assert(!state.semanticDiagnosticsPerFile); // We dont need to cache the diagnostics just return them from program - return programOfThisState.getSemanticDiagnostics(sourceFile, cancellationToken); + return state.program.getSemanticDiagnostics(sourceFile, cancellationToken); } if (sourceFile) { - return getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken); + return getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken); + } + + if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) { + // When semantic builder asks for diagnostics of the whole program, + // ensure that all the affected files are handled + let affected: SourceFile | Program | undefined; + while (affected = getNextAffectedFile(state, cancellationToken, computeHash)) { + doneWithAffectedFile(state, affected); + } } let diagnostics: Diagnostic[]; - for (const sourceFile of programOfThisState.getSourceFiles()) { - diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(programOfThisState, sourceFile, cancellationToken)); + for (const sourceFile of state.program.getSourceFiles()) { + diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken)); } return diagnostics || emptyArray; } - - /** - * Gets the semantic diagnostics either from cache if present, or otherwise from program and caches it - * Note that it is assumed that the when asked about semantic diagnostics, the file has been taken out of affected files/changed file set - */ - function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { - const path = sourceFile.path; - const cachedDiagnostics = state.semanticDiagnosticsPerFile.get(path); - // Report the semantic diagnostics from the cache if we already have those diagnostics present - if (cachedDiagnostics) { - return cachedDiagnostics; - } - - // Diagnostics werent cached, get them from program, and cache the result - const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); - state.semanticDiagnosticsPerFile.set(path, diagnostics); - return diagnostics; - } - - /** - * Get all the dependencies of the sourceFile - */ - function getAllDependencies(programOfThisState: Program, sourceFile: SourceFile) { - return BuilderState.getAllDependencies(state, programOfThisState, sourceFile); - } } } namespace ts { export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; - export interface BuilderHost { + export interface BuilderProgramHost { /** * return true if file names are treated with case sensitivity */ @@ -363,73 +409,110 @@ namespace ts { * If provided this would be used this hash instead of actual file shape text for detecting changes */ createHash?: (data: string) => string; + /** + * When emit or emitNextAffectedFile are called without writeFile, + * this callback if present would be used to write files + */ + writeFile?: WriteFileCallback; } /** * Builder to manage the program state changes */ - export interface BaseBuilder { + export interface BaseBuilderProgram { + /*@internal*/ + getState(): BuilderProgramState; /** - * Updates the program in the builder to represent new state + * Get compiler options of the program */ - updateProgram(newProgram: Program): void; - + getCompilerOptions(): CompilerOptions; + /** + * Get the source file in the program with file name + */ + getSourceFile(fileName: string): SourceFile | undefined; + /** + * Get a list of files in the program + */ + getSourceFiles(): ReadonlyArray; + /** + * Get the diagnostics for compiler options + */ + getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get the diagnostics that dont belong to any file + */ + getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get the syntax diagnostics, for all source files if source file is not supplied + */ + getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; /** * Get all the dependencies of the file */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; + getAllDependencies(sourceFile: SourceFile): ReadonlyArray; + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics + * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided, + * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics + */ + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Emits the JavaScript and declaration files. + * When targetSource file is specified, emits the files corresponding to that source file, + * otherwise for the whole program. + * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified, + * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified, + * it will only emit all the affected files instead of whole program + * + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files + */ + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; } /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files */ - export interface SemanticDiagnosticsBuilder extends BaseBuilder { + export interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { /** * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; - - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; } /** * The builder that can handle the changes in program and iterate through changed file to emit the files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ - export interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { + export interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + /** + * Get the current directory of the program + */ + getCurrentDirectory(): string; /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; - - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult; } /** * Create the builder to manage semantic diagnostics and cache them */ - export function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder { - return createBuilder(host, BuilderKind.BuilderKindSemanticDiagnostics); + export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram { + return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.SemanticDiagnosticsBuilderProgram); } /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - export function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder { - return createBuilder(host, BuilderKind.BuilderKindEmitAndSemanticDiagnostics); + export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram { + return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram); } } diff --git a/src/compiler/tsconfig.json b/src/compiler/tsconfig.json index 07f69ddfe28..92d9f099441 100644 --- a/src/compiler/tsconfig.json +++ b/src/compiler/tsconfig.json @@ -38,6 +38,7 @@ "emitter.ts", "watchUtilities.ts", "program.ts", + "builderState.ts", "builder.ts", "resolutionCache.ts", "watch.ts", diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 1c5a0ad5d99..c07722cb7d3 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -146,62 +146,23 @@ namespace ts { return ExitStatus.Success; } - /** - * Creates the function that emits files and reports errors when called with program - */ - function createEmitFilesAndReportErrorsWithBuilderUsingSystem(system: System, reportDiagnostic: DiagnosticReporter) { - const emitErrorsAndReportErrorsWithBuilder = createEmitFilesAndReportErrorsWithBuilder({ - useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, - createHash: system.createHash && (s => system.createHash(s)), - writeFile, - reportDiagnostic, - writeFileName: s => system.write(s + system.newLine) - }); - let host: CachedDirectoryStructureHost | undefined; - return { - emitFilesAndReportError: (program: Program) => emitErrorsAndReportErrorsWithBuilder(program), - setHost: (cachedDirectoryStructureHost: CachedDirectoryStructureHost) => host = cachedDirectoryStructureHost - }; - - function getHost() { - return host || system; - } - - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !getHost().directoryExists(directoryPath)) { - const parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - getHost().createDirectory(directoryPath); - } - } - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { - try { - performance.mark("beforeIOWrite"); - ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - - getHost().writeFile(fileName, text, writeByteOrderMark); - - performance.mark("afterIOWrite"); - performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); - } - catch (e) { - if (onError) { - onError(e.message); - } - } - } - } - const noopFileWatcher: FileWatcher = { close: noop }; /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter): WatchCompilerHost { - const { emitFilesAndReportError, setHost } = createEmitFilesAndReportErrorsWithBuilderUsingSystem(system, reportDiagnostic); - const host: WatchCompilerHost = { - useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + let host: DirectoryStructureHost = system; + const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames; + const writeFileName = (s: string) => system.write(s + system.newLine); + const builderProgramHost: BuilderProgramHost = { + useCaseSensitiveFileNames, + createHash: system.createHash && (s => system.createHash(s)), + writeFile + }; + let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined; + return { + useCaseSensitiveFileNames, getNewLine: () => system.newLine, getCurrentDirectory: () => system.getCurrentDirectory(), getDefaultLibLocation, @@ -220,10 +181,9 @@ namespace ts { onWatchStatusChange, createDirectory: path => system.createDirectory(path), writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), - onCachedDirectoryStructureHostCreate: host => setHost(host), - afterProgramCreate: emitFilesAndReportError, + onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system, + afterProgramCreate: emitFilesAndReportErrorUsingBuilder, }; - return host; function getDefaultLibLocation() { return getDirectoryPath(normalizePath(system.getExecutingFilePath())); @@ -235,6 +195,36 @@ namespace ts { } system.write(`${new Date().toLocaleTimeString()} - ${flattenDiagnosticMessageText(diagnostic.messageText, newLine)}${newLine + newLine + newLine}`); } + + function emitFilesAndReportErrorUsingBuilder(program: Program) { + builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, builderProgramHost, builderProgram); + emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName); + } + + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { + const parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + host.createDirectory(directoryPath); + } + } + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { + try { + performance.mark("beforeIOWrite"); + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + + host.writeFile(fileName, text, writeByteOrderMark); + + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + } } /** @@ -272,73 +262,6 @@ namespace ts { namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; - interface BuilderProgram extends ProgramToEmitFilesAndReportErrors { - updateProgram(program: Program): void; - } - - function createBuilderProgram(host: BuilderEmitHost): BuilderProgram { - const builder = createEmitAndSemanticDiagnosticsBuilder(host); - let program: Program; - return { - getCurrentDirectory: () => program.getCurrentDirectory(), - getCompilerOptions: () => program.getCompilerOptions(), - getSourceFiles: () => program.getSourceFiles(), - getSyntacticDiagnostics: () => program.getSyntacticDiagnostics(), - getOptionsDiagnostics: () => program.getOptionsDiagnostics(), - getGlobalDiagnostics: () => program.getGlobalDiagnostics(), - getSemanticDiagnostics: () => builder.getSemanticDiagnostics(program), - emit, - updateProgram - }; - - function updateProgram(p: Program) { - program = p; - builder.updateProgram(p); - } - - function emit(): EmitResult { - // Emit and report any errors we ran into. - let sourceMaps: SourceMapData[]; - let emitSkipped: boolean; - let diagnostics: Diagnostic[]; - let emittedFiles: string[]; - - let affectedEmitResult: AffectedFileResult; - while (affectedEmitResult = builder.emitNextAffectedFile(program, host.writeFile)) { - emitSkipped = emitSkipped || affectedEmitResult.result.emitSkipped; - diagnostics = addRange(diagnostics, affectedEmitResult.result.diagnostics); - emittedFiles = addRange(emittedFiles, affectedEmitResult.result.emittedFiles); - sourceMaps = addRange(sourceMaps, affectedEmitResult.result.sourceMaps); - } - return { - emitSkipped, - diagnostics, - emittedFiles, - sourceMaps - }; - } - } - - /** - * Host needed to emit files and report errors using builder - */ - export interface BuilderEmitHost extends BuilderHost { - writeFile: WriteFileCallback; - reportDiagnostic: DiagnosticReporter; - writeFileName?: (s: string) => void; - } - - /** - * Creates the function that reports the program errors and emit files every time it is called with argument as program - */ - export function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost) { - const builderProgram = createBuilderProgram(host); - return (program: Program) => { - builderProgram.updateProgram(program); - emitFilesAndReportErrors(builderProgram, host.reportDiagnostic, host.writeFileName); - }; - } - export interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; diff --git a/src/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index a6353d9c0c3..8808a151c04 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -81,20 +81,22 @@ namespace ts { }); function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { - const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); + const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue }; + let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined; return fileNames => { const program = getProgram(); - builder.updateProgram(program); + builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram); const outputFileNames: string[] = []; // tslint:disable-next-line no-empty - while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName))) { + while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName))) { } assert.deepEqual(outputFileNames, fileNames); }; } function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray, cancelAfterEmitLength?: number) => void { - const builder = createEmitAndSemanticDiagnosticsBuilder({ useCaseSensitiveFileNames: returnTrue, }); + const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue }; + let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined; let cancel = false; const cancellationToken: CancellationToken = { isCancellationRequested: () => cancel, @@ -108,7 +110,7 @@ namespace ts { cancel = false; let operationWasCancelled = false; const program = getProgram(); - builder.updateProgram(program); + builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram); const outputFileNames: string[] = []; try { // tslint:disable-next-line no-empty @@ -117,7 +119,7 @@ namespace ts { if (outputFileNames.length === cancelAfterEmitLength) { cancel = true; } - } while (builder.emitNextAffectedFile(program, fileName => outputFileNames.push(fileName), cancellationToken)); + } while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName), cancellationToken)); } catch (e) { assert.isFalse(operationWasCancelled); diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index ba944bafd9d..13a7a30d845 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -37,6 +37,7 @@ "../compiler/declarationEmitter.ts", "../compiler/emitter.ts", "../compiler/program.ts", + "../compiler/builderState.ts", "../compiler/builder.ts", "../compiler/resolutionCache.ts", "../compiler/watch.ts", diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 514c140f05e..59f3916bbfe 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3749,7 +3749,7 @@ declare namespace ts { result: T; affected: SourceFile | Program; } | undefined; - interface BuilderHost { + interface BuilderProgramHost { /** * return true if file names are treated with case sensitivity */ @@ -3758,78 +3758,104 @@ declare namespace ts { * If provided this would be used this hash instead of actual file shape text for detecting changes */ createHash?: (data: string) => string; + /** + * When emit or emitNextAffectedFile are called without writeFile, + * this callback if present would be used to write files + */ + writeFile?: WriteFileCallback; } /** * Builder to manage the program state changes */ - interface BaseBuilder { + interface BaseBuilderProgram { /** - * Updates the program in the builder to represent new state + * Get compiler options of the program */ - updateProgram(newProgram: Program): void; + getCompilerOptions(): CompilerOptions; + /** + * Get the source file in the program with file name + */ + getSourceFile(fileName: string): SourceFile | undefined; + /** + * Get a list of files in the program + */ + getSourceFiles(): ReadonlyArray; + /** + * Get the diagnostics for compiler options + */ + getOptionsDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get the diagnostics that dont belong to any file + */ + getGlobalDiagnostics(cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Get the syntax diagnostics, for all source files if source file is not supplied + */ + getSyntacticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; /** * Get all the dependencies of the file */ - getAllDependencies(programOfThisState: Program, sourceFile: SourceFile): ReadonlyArray; + getAllDependencies(sourceFile: SourceFile): ReadonlyArray; + /** + * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program + * The semantic diagnostics are cached and managed here + * Note that it is assumed that when asked about semantic diagnostics through this API, + * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics + * In case of SemanticDiagnosticsBuilderProgram if the source file is not provided, + * it will iterate through all the affected files, to ensure that cache stays valid and yet provide a way to get all semantic diagnostics + */ + getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + /** + * Emits the JavaScript and declaration files. + * When targetSource file is specified, emits the files corresponding to that source file, + * otherwise for the whole program. + * In case of EmitAndSemanticDiagnosticsBuilderProgram, when targetSourceFile is specified, + * it is assumed that that file is handled from affected file list. If targetSourceFile is not specified, + * it will only emit all the affected files instead of whole program + * + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files + */ + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; } /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files */ - interface SemanticDiagnosticsBuilder extends BaseBuilder { + interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { /** * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete */ - getSemanticDiagnosticsOfNextAffectedFile(programOfThisState: Program, cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + getSemanticDiagnosticsOfNextAffectedFile(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult>; } /** * The builder that can handle the changes in program and iterate through changed file to emit the files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ - interface EmitAndSemanticDiagnosticsBuilder extends BaseBuilder { + interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + /** + * Get the current directory of the program + */ + getCurrentDirectory(): string; /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete + * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host + * in that order would be used to write the files */ - emitNextAffectedFile(programOfThisState: Program, writeFileCallback: WriteFileCallback, cancellationToken?: CancellationToken, customTransformers?: CustomTransformers): AffectedFileResult; - /** - * Gets the semantic diagnostics from the program corresponding to this state of file (if provided) or whole program - * The semantic diagnostics are cached and managed here - * Note that it is assumed that the when asked about semantic diagnostics through this API, - * the file has been taken out of affected files so it is safe to use cache or get from program and cache the diagnostics - */ - getSemanticDiagnostics(programOfThisState: Program, sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray; + emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult; } /** * Create the builder to manage semantic diagnostics and cache them */ - function createSemanticDiagnosticsBuilder(host: BuilderHost): SemanticDiagnosticsBuilder; + function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - function createEmitAndSemanticDiagnosticsBuilder(host: BuilderHost): EmitAndSemanticDiagnosticsBuilder; + function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; } declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; - /** - * Host needed to emit files and report errors using builder - */ - interface BuilderEmitHost extends BuilderHost { - writeFile: WriteFileCallback; - reportDiagnostic: DiagnosticReporter; - writeFileName?: (s: string) => void; - } - /** - * Creates the function that reports the program errors and emit files every time it is called with argument as program - */ - function createEmitFilesAndReportErrorsWithBuilder(host: BuilderEmitHost): (program: Program) => void; interface WatchCompilerHost { /** If provided, callback to invoke before each program creation */ beforeProgramCreate?(compilerOptions: CompilerOptions): void; From 9b54d2e458248dfdd4dc5ff0078ccf9f56a2719b Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 19:20:05 -0800 Subject: [PATCH 032/100] Create api to create Watch --- src/compiler/builder.ts | 11 ++++- src/compiler/tsc.ts | 4 +- src/compiler/watch.ts | 42 +++++++++++++------ tests/baselines/reference/api/typescript.d.ts | 24 +++++++---- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 21c3b9021ee..d0a6c99179e 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -223,6 +223,14 @@ namespace ts { export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind) { + // Return same program if underlying program doesnt change + let oldState = oldProgram && oldProgram.getState(); + if (oldState && newProgram === oldState.program) { + newProgram = undefined; + oldState = undefined; + return oldProgram; + } + /** * Create the canonical file name for identity */ @@ -231,11 +239,12 @@ namespace ts { * Computing hash to for signature verification */ const computeHash = host.createHash || identity; - const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldProgram && oldProgram.getState()); + const state = createBuilderProgramState(newProgram, getCanonicalFileName, oldState); // To ensure that we arent storing any references to old program or new program without state newProgram = undefined; oldProgram = undefined; + oldState = undefined; const result: BaseBuilderProgram = { getState: () => state, diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index ed5284e696c..ec869f89ce2 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -165,13 +165,13 @@ namespace ts { watchCompilerHost.options = configParseResult.options; watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs; watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories; - createWatch(watchCompilerHost); + createWatchProgram(watchCompilerHost); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, reportDiagnostic); updateWatchCompilationHost(watchCompilerHost); - createWatch(watchCompilerHost); + createWatchProgram(watchCompilerHost); } function enableStatistics(compilerOptions: CompilerOptions) { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index c07722cb7d3..0b197c6a0f6 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -376,24 +376,24 @@ namespace ts { configFileWildCardDirectories?: MapLike; } - export interface Watch { + export interface Watch { /** Synchronize with host and get updated program */ - getProgram(): Program; + getProgram(): T; /** Gets the existing program without synchronizing with changes on host */ /*@internal*/ - getExistingProgram(): Program; + getExistingProgram(): T; } /** * Creates the watch what generates program using the config file */ - export interface WatchOfConfigFile extends Watch { + export interface WatchOfConfigFile extends Watch { } /** * Creates the watch that generates program using the root files and compiler options */ - export interface WatchOfFilesAndCompilerOptions extends Watch { + export interface WatchOfFilesAndCompilerOptions extends Watch { /** Updates the root files in the program, only if this is not config file compilation */ updateRootFileNames(fileNames: string[]): void; } @@ -401,26 +401,26 @@ namespace ts { /** * Create the watched program for config file */ - export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { - return createWatch(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic)); + export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile { + return createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic)); } /** * Create the watched program for root files and compiler options */ - export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { - return createWatch(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic)); + export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions { + return createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic)); } /** * Creates the watch from the host for root files and compiler options */ - export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; + export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - export function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; - export function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + export function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { interface HostFileInfo { version: number; sourceFile: SourceFile; @@ -890,4 +890,22 @@ namespace ts { ); } } + + /** + * Creates the watch from the host for root files and compiler options + */ + export function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; + /** + * Creates the watch from the host for config file + */ + export function createBuilderWatchProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; + export function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + const watch = createWatchProgram(host); + let builderProgram: T | undefined; + return { + getProgram: () => builderProgram = createBuilderProgram(watch.getProgram(), host, builderProgram), + getExistingProgram: () => builderProgram = createBuilderProgram(watch.getExistingProgram(), host, builderProgram), + updateRootFileNames: watch.updateRootFileNames && (fileNames => watch.updateRootFileNames(fileNames)) + }; + } } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 59f3916bbfe..9544057047a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3935,38 +3935,46 @@ declare namespace ts { */ readDirectory(path: string, extensions?: ReadonlyArray, exclude?: ReadonlyArray, include?: ReadonlyArray, depth?: number): string[]; } - interface Watch { + interface Watch { /** Synchronize with host and get updated program */ - getProgram(): Program; + getProgram(): T; } /** * Creates the watch what generates program using the config file */ - interface WatchOfConfigFile extends Watch { + interface WatchOfConfigFile extends Watch { } /** * Creates the watch that generates program using the root files and compiler options */ - interface WatchOfFilesAndCompilerOptions extends Watch { + interface WatchOfFilesAndCompilerOptions extends Watch { /** Updates the root files in the program, only if this is not config file compilation */ updateRootFileNames(fileNames: string[]): void; } /** * Create the watched program for config file */ - function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile; + function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfConfigFile; /** * Create the watched program for root files and compiler options */ - function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions; + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for root files and compiler options */ - function createWatch(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; + function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - function createWatch(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + /** + * Creates the watch from the host for root files and compiler options + */ + function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; + /** + * Creates the watch from the host for config file + */ + function createBuilderWatchProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; From 8ad9a6254c24742641df25b0d2c33a64888c0c77 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 19:44:47 -0800 Subject: [PATCH 033/100] Api to get underlying program from builder --- src/compiler/builder.ts | 5 +++++ tests/baselines/reference/api/typescript.d.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index d0a6c99179e..fdbfe32d450 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -248,6 +248,7 @@ namespace ts { const result: BaseBuilderProgram = { getState: () => state, + getProgram: () => state.program, getCompilerOptions: () => state.program.getCompilerOptions(), getSourceFile: fileName => state.program.getSourceFile(fileName), getSourceFiles: () => state.program.getSourceFiles(), @@ -431,6 +432,10 @@ namespace ts { export interface BaseBuilderProgram { /*@internal*/ getState(): BuilderProgramState; + /** + * Returns current program + */ + getProgram(): Program; /** * Get compiler options of the program */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 9544057047a..8014fe09702 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3768,6 +3768,10 @@ declare namespace ts { * Builder to manage the program state changes */ interface BaseBuilderProgram { + /** + * Returns current program + */ + getProgram(): Program; /** * Get compiler options of the program */ From a75badfd11c4d5fdca27142ce73c0935f4937221 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 7 Dec 2017 19:56:46 -0800 Subject: [PATCH 034/100] Rename on WatchBuilderProgram --- src/compiler/builder.ts | 14 +++++++------- src/compiler/watch.ts | 6 +++--- tests/baselines/reference/api/typescript.d.ts | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index fdbfe32d450..13c5a4a2ca4 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -220,9 +220,9 @@ namespace ts { EmitAndSemanticDiagnosticsBuilderProgram } - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BaseBuilderProgram | undefined, kind: BuilderProgramKind) { + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind) { // Return same program if underlying program doesnt change let oldState = oldProgram && oldProgram.getState(); if (oldState && newProgram === oldState.program) { @@ -246,7 +246,7 @@ namespace ts { oldProgram = undefined; oldState = undefined; - const result: BaseBuilderProgram = { + const result: BuilderProgram = { getState: () => state, getProgram: () => state.program, getCompilerOptions: () => state.program.getCompilerOptions(), @@ -429,7 +429,7 @@ namespace ts { /** * Builder to manage the program state changes */ - export interface BaseBuilderProgram { + export interface BuilderProgram { /*@internal*/ getState(): BuilderProgramState; /** @@ -490,7 +490,7 @@ namespace ts { /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files */ - export interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram { /** * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete @@ -502,7 +502,7 @@ namespace ts { * The builder that can handle the changes in program and iterate through changed file to emit the files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ - export interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + export interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { /** * Get the current directory of the program */ diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 0b197c6a0f6..abf29d59c8b 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -894,12 +894,12 @@ namespace ts { /** * Creates the watch from the host for root files and compiler options */ - export function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; + export function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - export function createBuilderWatchProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; - export function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + export function createWatchBuilderProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; + export function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { const watch = createWatchProgram(host); let builderProgram: T | undefined; return { diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 8014fe09702..4678e23d443 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3767,7 +3767,7 @@ declare namespace ts { /** * Builder to manage the program state changes */ - interface BaseBuilderProgram { + interface BuilderProgram { /** * Returns current program */ @@ -3825,7 +3825,7 @@ declare namespace ts { /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files */ - interface SemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + interface SemanticDiagnosticsBuilderProgram extends BuilderProgram { /** * Gets the semantic diagnostics from the program for the next affected file and caches it * Returns undefined if the iteration is complete @@ -3836,7 +3836,7 @@ declare namespace ts { * The builder that can handle the changes in program and iterate through changed file to emit the files * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ - interface EmitAndSemanticDiagnosticsBuilderProgram extends BaseBuilderProgram { + interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { /** * Get the current directory of the program */ @@ -3974,11 +3974,11 @@ declare namespace ts { /** * Creates the watch from the host for root files and compiler options */ - function createBuilderWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; + function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - function createBuilderWatchProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; + function createWatchBuilderProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; From cb2636679b9339a65d8d09771f1244078ba6c9f9 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 8 Dec 2017 12:35:37 -0800 Subject: [PATCH 035/100] When user provided resolution is used, invalidate resolutions for all files In this case there is no way to tell if resolution has changed so resolution cache wont have answers --- src/compiler/resolutionCache.ts | 6 +++--- src/compiler/watch.ts | 10 ++++++++-- tests/baselines/reference/api/typescript.d.ts | 2 ++ 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index 92eea4140da..d365ec21379 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -14,7 +14,7 @@ namespace ts { invalidateResolutionOfFile(filePath: Path): void; removeResolutionsOfFile(filePath: Path): void; - createHasInvalidatedResolution(): HasInvalidatedResolution; + createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; startCachingPerDirectoryResolution(): void; finishCachingPerDirectoryResolution(): void; @@ -159,8 +159,8 @@ namespace ts { return collected; } - function createHasInvalidatedResolution(): HasInvalidatedResolution { - if (allFilesHaveInvalidatedResolution) { + function createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution { + if (allFilesHaveInvalidatedResolution || forceAllFilesAsInvalidated) { // Any file asked would have invalidated resolution filesWithInvalidatedResolutions = undefined; return returnTrue; diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index abf29d59c8b..762487dc0c7 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -302,6 +302,8 @@ namespace ts { /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; /** Used to watch changes in source files, missing files needed to update the program or config file */ watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; @@ -520,7 +522,10 @@ namespace ts { compilerHost.resolveModuleNames = host.resolveModuleNames ? ((moduleNames, containingFile, reusedNames) => host.resolveModuleNames(moduleNames, containingFile, reusedNames)) : ((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames)); - compilerHost.resolveTypeReferenceDirectives = resolutionCache.resolveTypeReferenceDirectives.bind(resolutionCache); + compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ? + ((typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile)) : + ((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile)); + const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives; reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); synchronizeProgram(); @@ -542,7 +547,8 @@ namespace ts { } } - const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(); + // All resolutions are invalid if user provided resolutions + const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution); if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { return program; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 52b7d4bd707..427218aa390 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3894,6 +3894,8 @@ declare namespace ts { trace?(s: string): void; /** If provided, used to resolve the module names, otherwise typescript's default module resolution */ resolveModuleNames?(moduleNames: string[], containingFile: string, reusedNames?: string[]): ResolvedModule[]; + /** If provided, used to resolve type reference directives, otherwise typescript's default resolution */ + resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): ResolvedTypeReferenceDirective[]; /** Used to watch changes in source files, missing files needed to update the program or config file */ watchFile(path: string, callback: FileWatcherCallback, pollingInterval?: number): FileWatcher; /** Used to watch resolved module's failed lookup locations, config file specs, type roots where auto type reference directives are added */ From 5c889299f4ddb4ba53b704a423da01e2d21275b9 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 17 Jan 2018 13:21:10 -0800 Subject: [PATCH 036/100] Indexed access relation check object+index types Previously, it only check the object types, and only if the index types were identical. Now both checks call `isRelatedTo` recursively. --- src/compiler/checker.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 489fff16e2c..0cfb6d1a381 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9632,7 +9632,7 @@ namespace ts { } else if (target.flags & TypeFlags.IndexedAccess) { // A type S is related to a type T[K] if S is related to A[K], where K is string-like and - // A is the apparent type of T. + // A is the constraint of T. const constraint = getConstraintOfIndexedAccess(target); if (constraint) { if (result = isRelatedTo(source, constraint, reportErrors)) { @@ -9668,7 +9668,7 @@ namespace ts { } else if (source.flags & TypeFlags.IndexedAccess) { // A type S[K] is related to a type T if A[K] is related to T, where K is string-like and - // A is the apparent type of S. + // A is the constraint of S. const constraint = getConstraintOfIndexedAccess(source); if (constraint) { if (result = isRelatedTo(constraint, target, reportErrors)) { @@ -9676,10 +9676,11 @@ namespace ts { return result; } } - else if (target.flags & TypeFlags.IndexedAccess && (source).indexType === (target).indexType) { - // if we have indexed access types with identical index types, see if relationship holds for - // the two object types. + else if (target.flags & TypeFlags.IndexedAccess) { if (result = isRelatedTo((source).objectType, (target).objectType, reportErrors)) { + result &= isRelatedTo((source).indexType, (target).indexType, reportErrors); + } + if (result) { errorInfo = saveErrorInfo; return result; } From 485ec34e8ed7d3240acc567c2287dd822ca2aea8 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Wed, 17 Jan 2018 13:22:31 -0800 Subject: [PATCH 037/100] Test assignability of indexed access types --- .../reference/keyofAndIndexedAccess.js | 24 +++++ .../reference/keyofAndIndexedAccess.symbols | 45 +++++++++ .../reference/keyofAndIndexedAccess.types | 48 ++++++++++ .../keyofAndIndexedAccessErrors.errors.txt | 53 +++++++++-- .../reference/keyofAndIndexedAccessErrors.js | 37 ++++++-- .../keyofAndIndexedAccessErrors.symbols | 90 ++++++++++++++---- .../keyofAndIndexedAccessErrors.types | 93 ++++++++++++++++--- .../types/keyof/keyofAndIndexedAccess.ts | 13 +++ .../keyof/keyofAndIndexedAccessErrors.ts | 21 ++++- 9 files changed, 377 insertions(+), 47 deletions(-) diff --git a/tests/baselines/reference/keyofAndIndexedAccess.js b/tests/baselines/reference/keyofAndIndexedAccess.js index 8ea17554b35..2f59ad03510 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.js +++ b/tests/baselines/reference/keyofAndIndexedAccess.js @@ -552,6 +552,19 @@ class AnotherSampleClass extends SampleClass { } } new AnotherSampleClass({}); + +// Positive repro from #17166 +function f3(t: T, k: K, tk: T[K]): void { + for (let key in t) { + key = k // ok, K ==> keyof T + t[key] = tk; // ok, T[K] ==> T[keyof T] + } +} + +// # 21185 +type Predicates = { + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] +} //// [keyofAndIndexedAccess.js] @@ -928,6 +941,13 @@ var AnotherSampleClass = /** @class */ (function (_super) { return AnotherSampleClass; }(SampleClass)); new AnotherSampleClass({}); +// Positive repro from #17166 +function f3(t, k, tk) { + for (var key in t) { + key = k; // ok, K ==> keyof T + t[key] = tk; // ok, T[K] ==> T[keyof T] + } +} //// [keyofAndIndexedAccess.d.ts] @@ -1188,3 +1208,7 @@ declare class AnotherSampleClass extends SampleClass { constructor(props: T); brokenMethod(): void; } +declare function f3(t: T, k: K, tk: T[K]): void; +declare type Predicates = { + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T]; +}; diff --git a/tests/baselines/reference/keyofAndIndexedAccess.symbols b/tests/baselines/reference/keyofAndIndexedAccess.symbols index 461652fb725..c1a3449991f 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccess.symbols @@ -1962,3 +1962,48 @@ class AnotherSampleClass extends SampleClass { new AnotherSampleClass({}); >AnotherSampleClass : Symbol(AnotherSampleClass, Decl(keyofAndIndexedAccess.ts, 540, 54)) +// Positive repro from #17166 +function f3(t: T, k: K, tk: T[K]): void { +>f3 : Symbol(f3, Decl(keyofAndIndexedAccess.ts, 552, 27)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 555, 12)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 555, 14)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 555, 12)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 555, 34)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 555, 12)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 555, 39)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 555, 14)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccess.ts, 555, 45)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 555, 12)) +>K : Symbol(K, Decl(keyofAndIndexedAccess.ts, 555, 14)) + + for (let key in t) { +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 556, 12)) +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 555, 34)) + + key = k // ok, K ==> keyof T +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 556, 12)) +>k : Symbol(k, Decl(keyofAndIndexedAccess.ts, 555, 39)) + + t[key] = tk; // ok, T[K] ==> T[keyof T] +>t : Symbol(t, Decl(keyofAndIndexedAccess.ts, 555, 34)) +>key : Symbol(key, Decl(keyofAndIndexedAccess.ts, 556, 12)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccess.ts, 555, 45)) + } +} + +// # 21185 +type Predicates = { +>Predicates : Symbol(Predicates, Decl(keyofAndIndexedAccess.ts, 560, 1)) +>TaggedRecord : Symbol(TaggedRecord, Decl(keyofAndIndexedAccess.ts, 563, 16)) + + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 564, 3)) +>TaggedRecord : Symbol(TaggedRecord, Decl(keyofAndIndexedAccess.ts, 563, 16)) +>variant : Symbol(variant, Decl(keyofAndIndexedAccess.ts, 564, 30)) +>TaggedRecord : Symbol(TaggedRecord, Decl(keyofAndIndexedAccess.ts, 563, 16)) +>TaggedRecord : Symbol(TaggedRecord, Decl(keyofAndIndexedAccess.ts, 563, 16)) +>variant : Symbol(variant, Decl(keyofAndIndexedAccess.ts, 564, 30)) +>TaggedRecord : Symbol(TaggedRecord, Decl(keyofAndIndexedAccess.ts, 563, 16)) +>T : Symbol(T, Decl(keyofAndIndexedAccess.ts, 564, 3)) +} + diff --git a/tests/baselines/reference/keyofAndIndexedAccess.types b/tests/baselines/reference/keyofAndIndexedAccess.types index 0e7da19c3e2..d60245e183f 100644 --- a/tests/baselines/reference/keyofAndIndexedAccess.types +++ b/tests/baselines/reference/keyofAndIndexedAccess.types @@ -2294,3 +2294,51 @@ new AnotherSampleClass({}); >AnotherSampleClass : typeof AnotherSampleClass >{} : {} +// Positive repro from #17166 +function f3(t: T, k: K, tk: T[K]): void { +>f3 : (t: T, k: K, tk: T[K]) => void +>T : T +>K : K +>T : T +>t : T +>T : T +>k : K +>K : K +>tk : T[K] +>T : T +>K : K + + for (let key in t) { +>key : keyof T +>t : T + + key = k // ok, K ==> keyof T +>key = k : K +>key : keyof T +>k : K + + t[key] = tk; // ok, T[K] ==> T[keyof T] +>t[key] = tk : T[K] +>t[key] : T[keyof T] +>t : T +>key : keyof T +>tk : T[K] + } +} + +// # 21185 +type Predicates = { +>Predicates : Predicates +>TaggedRecord : TaggedRecord + + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] +>T : T +>TaggedRecord : TaggedRecord +>variant : TaggedRecord[keyof TaggedRecord] +>TaggedRecord : TaggedRecord +>TaggedRecord : TaggedRecord +>variant : any +>TaggedRecord : TaggedRecord +>T : T +} + diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt index 2981cb0366d..0b2da3f8e61 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.errors.txt @@ -27,11 +27,21 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(76,5): error Type 'T' is not assignable to type 'T & U'. Type 'T' is not assignable to type 'U'. tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(77,5): error TS2322: Type 'keyof (T & U)' is not assignable to type 'keyof (T | U)'. -tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(84,9): error TS2322: Type 'keyof T' is not assignable to type 'K'. -tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(85,9): error TS2322: Type 'T[keyof T]' is not assignable to type 'T[K]'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(86,9): error TS2322: Type 'keyof T' is not assignable to type 'K'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(88,9): error TS2322: Type 'T[keyof T]' is not assignable to type 'T[K]'. + Type 'keyof T' is not assignable to type 'K'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(91,5): error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. + Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(94,5): error TS2322: Type 'T[J]' is not assignable to type 'U[J]'. + Type 'T' is not assignable to type 'U'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(97,5): error TS2322: Type 'T[K]' is not assignable to type 'T[J]'. + Type 'K' is not assignable to type 'J'. + Type 'keyof T' is not assignable to type 'J'. +tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(100,5): error TS2322: Type 'T[K]' is not assignable to type 'U[J]'. + Type 'T' is not assignable to type 'U'. -==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (27 errors) ==== +==== tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts (31 errors) ==== class Shape { name: string; width: number; @@ -167,15 +177,42 @@ tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts(85,9): error } // Repro from #17166 - function f3(obj: T, k: K, value: T[K]): void { - for (let key in obj) { + function f3( + t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]): void { + for (let key in t) { + key = k // ok, K ==> keyof T k = key // error, keyof T =/=> K ~ !!! error TS2322: Type 'keyof T' is not assignable to type 'K'. - value = obj[key]; // error, T[keyof T] =/=> T[K] - ~~~~~ + t[key] = tk; // ok, T[K] ==> T[keyof T] + tk = t[key]; // error, T[keyof T] =/=> T[K] + ~~ !!! error TS2322: Type 'T[keyof T]' is not assignable to type 'T[K]'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'K'. } - } + tk = uk; + uk = tk; // error + ~~ +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[K]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + tj = uj; + uj = tj; // error + ~~ +!!! error TS2322: Type 'T[J]' is not assignable to type 'U[J]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + + tk = tj; + tj = tk; // error + ~~ +!!! error TS2322: Type 'T[K]' is not assignable to type 'T[J]'. +!!! error TS2322: Type 'K' is not assignable to type 'J'. +!!! error TS2322: Type 'keyof T' is not assignable to type 'J'. + + tk = uj; + uj = tk; // error + ~~ +!!! error TS2322: Type 'T[K]' is not assignable to type 'U[J]'. +!!! error TS2322: Type 'T' is not assignable to type 'U'. + } \ No newline at end of file diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.js b/tests/baselines/reference/keyofAndIndexedAccessErrors.js index 2a1042cf4ee..d267699ffd6 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.js +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.js @@ -80,13 +80,26 @@ function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { } // Repro from #17166 -function f3(obj: T, k: K, value: T[K]): void { - for (let key in obj) { +function f3( + t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]): void { + for (let key in t) { + key = k // ok, K ==> keyof T k = key // error, keyof T =/=> K - value = obj[key]; // error, T[keyof T] =/=> T[K] + t[key] = tk; // ok, T[K] ==> T[keyof T] + tk = t[key]; // error, T[keyof T] =/=> T[K] } -} + tk = uk; + uk = tk; // error + tj = uj; + uj = tj; // error + + tk = tj; + tj = tk; // error + + tk = uj; + uj = tk; // error +} //// [keyofAndIndexedAccessErrors.js] @@ -120,9 +133,19 @@ function f20(k1, k2, o1, o2) { k2 = k1; } // Repro from #17166 -function f3(obj, k, value) { - for (var key in obj) { +function f3(t, k, tk, u, j, uk, tj, uj) { + for (var key in t) { + key = k; // ok, K ==> keyof T k = key; // error, keyof T =/=> K - value = obj[key]; // error, T[keyof T] =/=> T[K] + t[key] = tk; // ok, T[K] ==> T[keyof T] + tk = t[key]; // error, T[keyof T] =/=> T[K] } + tk = uk; + uk = tk; // error + tj = uj; + uj = tj; // error + tk = tj; + tj = tk; // error + tk = uj; + uj = tk; // error } diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols b/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols index 0e97ef793fb..47b22af2541 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.symbols @@ -270,32 +270,90 @@ function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { } // Repro from #17166 -function f3(obj: T, k: K, value: T[K]): void { +function f3( >f3 : Symbol(f3, Decl(keyofAndIndexedAccessErrors.ts, 78, 1)) >T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) >K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) >T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccessErrors.ts, 81, 34)) ->T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) ->k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 81, 41)) ->K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) ->value : Symbol(value, Decl(keyofAndIndexedAccessErrors.ts, 81, 47)) +>U : Symbol(U, Decl(keyofAndIndexedAccessErrors.ts, 81, 33)) >T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) +>J : Symbol(J, Decl(keyofAndIndexedAccessErrors.ts, 81, 46)) >K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) - for (let key in obj) { ->key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 82, 12)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccessErrors.ts, 81, 34)) + t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]): void { +>t : Symbol(t, Decl(keyofAndIndexedAccessErrors.ts, 81, 60)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) +>k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 82, 9)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) +>u : Symbol(u, Decl(keyofAndIndexedAccessErrors.ts, 82, 25)) +>U : Symbol(U, Decl(keyofAndIndexedAccessErrors.ts, 81, 33)) +>j : Symbol(j, Decl(keyofAndIndexedAccessErrors.ts, 82, 31)) +>J : Symbol(J, Decl(keyofAndIndexedAccessErrors.ts, 81, 46)) +>uk : Symbol(uk, Decl(keyofAndIndexedAccessErrors.ts, 82, 37)) +>U : Symbol(U, Decl(keyofAndIndexedAccessErrors.ts, 81, 33)) +>K : Symbol(K, Decl(keyofAndIndexedAccessErrors.ts, 81, 14)) +>tj : Symbol(tj, Decl(keyofAndIndexedAccessErrors.ts, 82, 47)) +>T : Symbol(T, Decl(keyofAndIndexedAccessErrors.ts, 81, 12)) +>J : Symbol(J, Decl(keyofAndIndexedAccessErrors.ts, 81, 46)) +>uj : Symbol(uj, Decl(keyofAndIndexedAccessErrors.ts, 82, 57)) +>U : Symbol(U, Decl(keyofAndIndexedAccessErrors.ts, 81, 33)) +>J : Symbol(J, Decl(keyofAndIndexedAccessErrors.ts, 81, 46)) + + for (let key in t) { +>key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 83, 12)) +>t : Symbol(t, Decl(keyofAndIndexedAccessErrors.ts, 81, 60)) + + key = k // ok, K ==> keyof T +>key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 83, 12)) +>k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 82, 9)) k = key // error, keyof T =/=> K ->k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 81, 41)) ->key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 82, 12)) +>k : Symbol(k, Decl(keyofAndIndexedAccessErrors.ts, 82, 9)) +>key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 83, 12)) - value = obj[key]; // error, T[keyof T] =/=> T[K] ->value : Symbol(value, Decl(keyofAndIndexedAccessErrors.ts, 81, 47)) ->obj : Symbol(obj, Decl(keyofAndIndexedAccessErrors.ts, 81, 34)) ->key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 82, 12)) + t[key] = tk; // ok, T[K] ==> T[keyof T] +>t : Symbol(t, Decl(keyofAndIndexedAccessErrors.ts, 81, 60)) +>key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 83, 12)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) + + tk = t[key]; // error, T[keyof T] =/=> T[K] +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) +>t : Symbol(t, Decl(keyofAndIndexedAccessErrors.ts, 81, 60)) +>key : Symbol(key, Decl(keyofAndIndexedAccessErrors.ts, 83, 12)) } + tk = uk; +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) +>uk : Symbol(uk, Decl(keyofAndIndexedAccessErrors.ts, 82, 37)) + + uk = tk; // error +>uk : Symbol(uk, Decl(keyofAndIndexedAccessErrors.ts, 82, 37)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) + + tj = uj; +>tj : Symbol(tj, Decl(keyofAndIndexedAccessErrors.ts, 82, 47)) +>uj : Symbol(uj, Decl(keyofAndIndexedAccessErrors.ts, 82, 57)) + + uj = tj; // error +>uj : Symbol(uj, Decl(keyofAndIndexedAccessErrors.ts, 82, 57)) +>tj : Symbol(tj, Decl(keyofAndIndexedAccessErrors.ts, 82, 47)) + + tk = tj; +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) +>tj : Symbol(tj, Decl(keyofAndIndexedAccessErrors.ts, 82, 47)) + + tj = tk; // error +>tj : Symbol(tj, Decl(keyofAndIndexedAccessErrors.ts, 82, 47)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) + + tk = uj; +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) +>uj : Symbol(uj, Decl(keyofAndIndexedAccessErrors.ts, 82, 57)) + + uj = tk; // error +>uj : Symbol(uj, Decl(keyofAndIndexedAccessErrors.ts, 82, 57)) +>tk : Symbol(tk, Decl(keyofAndIndexedAccessErrors.ts, 82, 15)) } - diff --git a/tests/baselines/reference/keyofAndIndexedAccessErrors.types b/tests/baselines/reference/keyofAndIndexedAccessErrors.types index f5cc7c093d9..0c51d3a575c 100644 --- a/tests/baselines/reference/keyofAndIndexedAccessErrors.types +++ b/tests/baselines/reference/keyofAndIndexedAccessErrors.types @@ -301,35 +301,104 @@ function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { } // Repro from #17166 -function f3(obj: T, k: K, value: T[K]): void { ->f3 : (obj: T, k: K, value: T[K]) => void +function f3( +>f3 : (t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]) => void >T : T >K : K >T : T ->obj : T +>U : U +>T : T +>J : J +>K : K + + t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]): void { +>t : T >T : T >k : K >K : K ->value : T[K] +>tk : T[K] >T : T >K : K +>u : U +>U : U +>j : J +>J : J +>uk : U[K] +>U : U +>K : K +>tj : T[J] +>T : T +>J : J +>uj : U[J] +>U : U +>J : J - for (let key in obj) { + for (let key in t) { >key : keyof T ->obj : T +>t : T + + key = k // ok, K ==> keyof T +>key = k : K +>key : keyof T +>k : K k = key // error, keyof T =/=> K >k = key : keyof T >k : K >key : keyof T - value = obj[key]; // error, T[keyof T] =/=> T[K] ->value = obj[key] : T[keyof T] ->value : T[K] ->obj[key] : T[keyof T] ->obj : T + t[key] = tk; // ok, T[K] ==> T[keyof T] +>t[key] = tk : T[K] +>t[key] : T[keyof T] +>t : T +>key : keyof T +>tk : T[K] + + tk = t[key]; // error, T[keyof T] =/=> T[K] +>tk = t[key] : T[keyof T] +>tk : T[K] +>t[key] : T[keyof T] +>t : T >key : keyof T } + tk = uk; +>tk = uk : U[K] +>tk : T[K] +>uk : U[K] + + uk = tk; // error +>uk = tk : T[K] +>uk : U[K] +>tk : T[K] + + tj = uj; +>tj = uj : U[J] +>tj : T[J] +>uj : U[J] + + uj = tj; // error +>uj = tj : T[J] +>uj : U[J] +>tj : T[J] + + tk = tj; +>tk = tj : T[J] +>tk : T[K] +>tj : T[J] + + tj = tk; // error +>tj = tk : T[K] +>tj : T[J] +>tk : T[K] + + tk = uj; +>tk = uj : U[J] +>tk : T[K] +>uj : U[J] + + uj = tk; // error +>uj = tk : T[K] +>uj : U[J] +>tk : T[K] } - diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts index 70b70dff6ea..9ec4e820f73 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccess.ts @@ -554,3 +554,16 @@ class AnotherSampleClass extends SampleClass { } } new AnotherSampleClass({}); + +// Positive repro from #17166 +function f3(t: T, k: K, tk: T[K]): void { + for (let key in t) { + key = k // ok, K ==> keyof T + t[key] = tk; // ok, T[K] ==> T[keyof T] + } +} + +// # 21185 +type Predicates = { + [T in keyof TaggedRecord]: (variant: TaggedRecord[keyof TaggedRecord]) => variant is TaggedRecord[T] +} diff --git a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts index ffc5076831a..f96bcdb6234 100644 --- a/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts +++ b/tests/cases/conformance/types/keyof/keyofAndIndexedAccessErrors.ts @@ -79,10 +79,23 @@ function f20(k1: keyof (T | U), k2: keyof (T & U), o1: T | U, o2: T & U) { } // Repro from #17166 -function f3(obj: T, k: K, value: T[K]): void { - for (let key in obj) { +function f3( + t: T, k: K, tk: T[K], u: U, j: J, uk: U[K], tj: T[J], uj: U[J]): void { + for (let key in t) { + key = k // ok, K ==> keyof T k = key // error, keyof T =/=> K - value = obj[key]; // error, T[keyof T] =/=> T[K] + t[key] = tk; // ok, T[K] ==> T[keyof T] + tk = t[key]; // error, T[keyof T] =/=> T[K] } -} + tk = uk; + uk = tk; // error + tj = uj; + uj = tj; // error + + tk = tj; + tj = tk; // error + + tk = uj; + uj = tk; // error +} From b4a382bdd2a9cb25555e1f852b9e759372bc996f Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 11 Jan 2018 14:53:31 -0800 Subject: [PATCH 038/100] Stop explicitly storing newline in refactoring/code fix contexts It's already in the EditorSettings and the LanguageServiceHost. Fixes #18291 Fixes #18445 --- src/harness/unittests/extractTestHelpers.ts | 2 -- src/services/codeFixProvider.ts | 5 ++-- .../addMissingInvocationForDecorator.ts | 2 +- ...correctQualifiedNameToIndexedAccessType.ts | 2 +- .../codefixes/disableJsDiagnostics.ts | 6 +++-- src/services/codefixes/fixAddMissingMember.ts | 14 +++++----- .../codefixes/fixAwaitInSyncFunction.ts | 2 +- ...sDoesntImplementInheritedAbstractMember.ts | 2 +- .../fixClassIncorrectlyImplementsInterface.ts | 2 +- .../fixClassSuperMustPrecedeThisAccess.ts | 2 +- .../fixConstructorForDerivedNeedSuperCall.ts | 2 +- .../fixExtendsInterfaceBecomesImplements.ts | 2 +- .../fixForgottenThisPropertyAccess.ts | 2 +- .../codefixes/fixInvalidImportSyntax.ts | 6 ++--- src/services/codefixes/fixSpelling.ts | 2 +- src/services/codefixes/fixUnusedIdentifier.ts | 4 +-- src/services/codefixes/importFixes.ts | 27 +++++++++---------- src/services/completions.ts | 1 - src/services/refactorProvider.ts | 20 ++++++++++++-- .../refactors/annotateWithTypeFromJSDoc.ts | 4 +-- .../refactors/convertFunctionToEs6Class.ts | 2 +- src/services/refactors/convertToEs6Module.ts | 2 +- src/services/refactors/extractSymbol.ts | 4 +-- src/services/refactors/useDefaultImport.ts | 2 +- src/services/services.ts | 7 ++--- 25 files changed, 67 insertions(+), 59 deletions(-) diff --git a/src/harness/unittests/extractTestHelpers.ts b/src/harness/unittests/extractTestHelpers.ts index a04f443c3c9..f519555bd26 100644 --- a/src/harness/unittests/extractTestHelpers.ts +++ b/src/harness/unittests/extractTestHelpers.ts @@ -121,7 +121,6 @@ namespace ts { const sourceFile = program.getSourceFile(path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse }, - newLineCharacter, program, file: sourceFile, startPosition: selectionRange.start, @@ -185,7 +184,6 @@ namespace ts { const sourceFile = program.getSourceFile(f.path); const context: RefactorContext = { cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse }, - newLineCharacter, program, file: sourceFile, startPosition: selectionRange.start, diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index aa1f2fb6885..619bcc8813c 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -7,10 +7,9 @@ namespace ts { getAllCodeActions?(context: CodeFixAllContext): CombinedCodeActions; } - export interface CodeFixContextBase extends textChanges.TextChangesContext { + export interface CodeFixContextBase extends RefactorOrCodeFixContext { sourceFile: SourceFile; program: Program; - host: LanguageServiceHost; cancellationToken: CancellationToken; } @@ -84,7 +83,7 @@ namespace ts { export function codeFixAll(context: CodeFixAllContext, errorCodes: number[], use: (changes: textChanges.ChangeTracker, error: Diagnostic, commands: Push) => void): CombinedCodeActions { const commands: CodeActionCommand[] = []; - const changes = textChanges.ChangeTracker.with(context, t => + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands); } diff --git a/src/services/codefixes/addMissingInvocationForDecorator.ts b/src/services/codefixes/addMissingInvocationForDecorator.ts index d063df87be4..25214f08218 100644 --- a/src/services/codefixes/addMissingInvocationForDecorator.ts +++ b/src/services/codefixes/addMissingInvocationForDecorator.ts @@ -5,7 +5,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions: (context) => { - const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => makeChange(t, context.sourceFile, context.span.start)); return [{ description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index de4498035be..076ea6135ae 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const qualifiedName = getQualifiedName(context.sourceFile, context.span.start); if (!qualifiedName) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, context.sourceFile, qualifiedName)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${qualifiedName.left.text}["${qualifiedName.right.text}"]`]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 6a59e61d52d..cf006a6066c 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -9,12 +9,14 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions(context) { - const { sourceFile, program, newLineCharacter, span } = context; + const { sourceFile, program, span } = context; if (!isInJavaScriptFile(sourceFile) || !isCheckJsEnabledForFile(sourceFile, program.getCompilerOptions())) { return undefined; } + const newLineCharacter = getNewLineFromContext(context); + return [{ description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message), changes: [createFileTextChanges(sourceFile.fileName, [getIgnoreCommentLocationForLocation(sourceFile, span.start, newLineCharacter)])], @@ -36,7 +38,7 @@ namespace ts.codefix { fixIds: [fixId], // No point applying as a group, doing it once will fix all errors getAllCodeActions: context => codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { if (err.start !== undefined) { - changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, context.newLineCharacter)); + changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineFromContext(context))); } }), }); diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 0fc6a430e19..8e45efa09ad 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -96,7 +96,7 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined { - const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]); return { description, changes, fixId }; @@ -142,9 +142,9 @@ namespace ts.codefix { return typeNode || createKeywordTypeNode(SyntaxKind.AnyKeyword); } - function createAddPropertyDeclarationAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]); - const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); return { description, changes, fixId }; } @@ -159,7 +159,7 @@ namespace ts.codefix { changeTracker.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, property); } - function createAddIndexSignatureAction(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { + function createAddIndexSignatureAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, typeNode: TypeNode): CodeFixAction { // Index signatures cannot have the static modifier. const stringTypeNode = createKeywordTypeNode(SyntaxKind.StringKeyword); const indexingParameter = createParameter( @@ -176,14 +176,14 @@ namespace ts.codefix { [indexingParameter], typeNode); - const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); // No fixId here because code-fix-all currently only works on adding individual named properties. return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined }; } - function getActionForMethodDeclaration(context: textChanges.TextChangesContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { + function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); return { description, changes, fixId }; } diff --git a/src/services/codefixes/fixAwaitInSyncFunction.ts b/src/services/codefixes/fixAwaitInSyncFunction.ts index 883993e7b51..7d09e5f214f 100644 --- a/src/services/codefixes/fixAwaitInSyncFunction.ts +++ b/src/services/codefixes/fixAwaitInSyncFunction.ts @@ -11,7 +11,7 @@ namespace ts.codefix { const { sourceFile, span } = context; const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, nodes)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index da3685339ab..6e215666ddf 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -9,7 +9,7 @@ namespace ts.codefix { errorCodes, getCodeActions(context) { const { program, sourceFile, span } = context; - const changes = textChanges.ChangeTracker.with(context, t => + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 0236b0c79b7..1eb5bfac986 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 1595bbf3c13..76c1c588097 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; const { constructor, superCall } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, constructor, superCall)); return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index 8fe2e8458a8..0b747344e3b 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, span } = context; const ctr = getNode(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, ctr)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index d98ca556f47..a4e91410db5 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, context.span.start); if (!nodes) return undefined; const { extendsToken, heritageClauses } = nodes; - const changes = textChanges.ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChanges(t, sourceFile, extendsToken, heritageClauses)); return [{ description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 19610da0b15..8c337c16c4f 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile } = context; const token = getNode(sourceFile, context.span.start); - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, token)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, token)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index f98f1eaea7c..0bfc6a6090d 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -32,7 +32,7 @@ namespace ts.codefix { createImportClause(namespace.name, /*namedBindings*/ undefined), node.moduleSpecifier ); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -48,7 +48,7 @@ namespace ts.codefix { namespace.name, createExternalModuleReference(node.moduleSpecifier) ); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -86,7 +86,7 @@ namespace ts.codefix { addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport)); } const propertyAccess = createPropertyAccess(expr, "default"); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); changeTracker.replaceNode(sourceFile, expr, propertyAccess, {}); const changes = changeTracker.getChanges(); fixes.push({ diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 729f14e9ef9..95159012f48 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -12,7 +12,7 @@ namespace ts.codefix { const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { node, suggestion } = info; - const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion)); + const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, node, suggestion)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index b7d948b3deb..93c8ae80ac9 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -13,13 +13,13 @@ namespace ts.codefix { const token = getToken(sourceFile, context.span.start); const result: CodeFixAction[] = []; - const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(t, sourceFile, token)); + const deletion = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryDeleteDeclaration(t, sourceFile, token)); if (deletion.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), [token.getText()]); result.push({ description, changes: deletion, fixId: fixIdDelete }); } - const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); + const prefix = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); if (prefix.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Prefix_0_with_an_underscore), [token.getText()]); result.push({ description, changes: prefix, fixId: fixIdPrefix }); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 8f69cd95c11..204030e3104 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -24,16 +24,14 @@ namespace ts.codefix { moduleSpecifier?: string; } - interface SymbolContext extends textChanges.TextChangesContext { + interface SymbolContext { sourceFile: SourceFile; symbolName: string; + formatContext: ts.formatting.FormatContext; } - interface SymbolAndTokenContext extends SymbolContext { + interface ImportCodeFixContext extends SymbolContext { symbolToken: Identifier | undefined; - } - - interface ImportCodeFixContext extends SymbolAndTokenContext { host: LanguageServiceHost; program: Program; checker: TypeChecker; @@ -173,7 +171,6 @@ namespace ts.codefix { const symbolToken = cast(getTokenAtPosition(context.sourceFile, context.span.start, /*includeJsDocComment*/ false), isIdentifier); return { host: context.host, - newLineCharacter: context.newLineCharacter, formatContext: context.formatContext, sourceFile: context.sourceFile, program, @@ -260,7 +257,7 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { + function getCodeActionForNewImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { const { kind, sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); @@ -278,7 +275,7 @@ namespace ts.codefix { createIdentifier(symbolName), createExternalModuleReference(quotedModuleSpecifier)); - const changes = ChangeTracker.with(context, changeTracker => { + const changes = ChangeTracker.with(toTextChangesContext(context), changeTracker => { if (lastImportDeclaration) { changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl); } @@ -672,33 +669,33 @@ namespace ts.codefix { return expression && isStringLiteral(expression) ? expression.text : undefined; } - function tryUpdateExistingImport(context: SymbolContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { + function tryUpdateExistingImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { const { symbolName, sourceFile, kind } = context; const { name } = importClause; const { namedBindings } = importClause.kind !== SyntaxKind.ImportEqualsDeclaration && importClause; switch (kind) { case ImportKind.Default: - return name ? undefined : ChangeTracker.with(context, t => + return name ? undefined : ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(createIdentifier(symbolName), namedBindings))); case ImportKind.Named: { const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName)); if (namedBindings && namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length !== 0) { // There are already named imports; add another. - return ChangeTracker.with(context, t => t.insertNodeInListAfter( + return ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeInListAfter( sourceFile, namedBindings.elements[namedBindings.elements.length - 1], newImportSpecifier)); } if (!namedBindings || namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length === 0) { - return ChangeTracker.with(context, t => + return ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamedImports([newImportSpecifier])))); } return undefined; } case ImportKind.Namespace: - return namedBindings ? undefined : ChangeTracker.with(context, t => + return namedBindings ? undefined : ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamespaceImport(createIdentifier(symbolName))))); case ImportKind.Equals: @@ -709,7 +706,7 @@ namespace ts.codefix { } } - function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext, symbolToken: Identifier): ImportCodeAction { + function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext & RefactorOrCodeFixContext, symbolToken: Identifier): ImportCodeAction { const { symbolName, sourceFile } = context; /** @@ -723,7 +720,7 @@ namespace ts.codefix { * become "ns.foo" */ // Prefix the node instead of it replacing it, because this may be used for import completions and we don't want the text changes to overlap with the identifier being completed. - const changes = ChangeTracker.with(context, tracker => + const changes = ChangeTracker.with(toTextChangesContext(context), tracker => tracker.changeIdentifierToPropertyAccess(sourceFile, namespacePrefix, symbolToken)); return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes, "CodeChange", /*moduleSpecifier*/ undefined); } diff --git a/src/services/completions.ts b/src/services/completions.ts index 36a10a1b578..82e7065228a 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -627,7 +627,6 @@ namespace ts.Completions { host, program, checker, - newLineCharacter: host.getNewLine(), compilerOptions, sourceFile, formatContext, diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 85ef9113bda..3d2a60e63e9 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -14,12 +14,28 @@ namespace ts { getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined; } - export interface RefactorContext extends textChanges.TextChangesContext { + export interface RefactorOrCodeFixContext { + host: LanguageServiceHost; + formatContext: ts.formatting.FormatContext; + } + + export function getNewLineFromContext(context: RefactorOrCodeFixContext) { + const formatSettings = context.formatContext.options; + return formatSettings ? formatSettings.newLineCharacter : context.host.getNewLine(); + } + + export function toTextChangesContext(context: RefactorOrCodeFixContext): textChanges.TextChangesContext { + return { + newLineCharacter: getNewLineFromContext(context), + formatContext: context.formatContext, + }; + } + + export interface RefactorContext extends RefactorOrCodeFixContext { file: SourceFile; startPosition: number; endPosition?: number; program: Program; - host: LanguageServiceHost; cancellationToken?: CancellationToken; } diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index d3bf59638b2..a39cdc20c42 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -78,7 +78,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { return Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`); } - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); suppressLeadingAndTrailingTrivia(declarationWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType); @@ -93,7 +93,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { const sourceFile = context.file; const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); const decl = findAncestor(token, isFunctionLikeDeclaration); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const functionWithType = addTypesToFunctionLike(decl); suppressLeadingAndTrailingTrivia(functionWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType); diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index cddf40ae017..93e39bc683c 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -59,7 +59,7 @@ namespace ts.refactor.convertFunctionToES6Class { } const ctorDeclaration = ctorSymbol.valueDeclaration; - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); let precedingNode: Node; let newClassDeclaration: ClassDeclaration; diff --git a/src/services/refactors/convertToEs6Module.ts b/src/services/refactors/convertToEs6Module.ts index 1046bf90aa6..933797554dd 100644 --- a/src/services/refactors/convertToEs6Module.ts +++ b/src/services/refactors/convertToEs6Module.ts @@ -74,7 +74,7 @@ namespace ts.refactor { Debug.assertEqual(actionName, _actionName); const { file, program } = context; Debug.assert(isSourceFileJavaScript(file)); - const edits = textChanges.ChangeTracker.with(context, changes => { + const edits = textChanges.ChangeTracker.with(toTextChangesContext(context), changes => { const moduleExportsChangedToDefault = convertFileToEs6Module(file, program.getTypeChecker(), changes, program.getCompilerOptions().target); if (moduleExportsChangedToDefault) { for (const importingFile of program.getSourceFiles()) { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index b3110a0ca36..9a9c30756a4 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -806,7 +806,7 @@ namespace ts.refactor.extractSymbol { ); } - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { @@ -1011,7 +1011,7 @@ namespace ts.refactor.extractSymbol { const initializer = transformConstantInitializer(node, substitutions); suppressLeadingAndTrailingTrivia(initializer); - const changeTracker = textChanges.ChangeTracker.fromContext(context); + const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); if (isClassLike(scope)) { Debug.assert(!isJS); // See CannotExtractToJSClass diff --git a/src/services/refactors/useDefaultImport.ts b/src/services/refactors/useDefaultImport.ts index a103168f67b..e64eaf0bfcb 100644 --- a/src/services/refactors/useDefaultImport.ts +++ b/src/services/refactors/useDefaultImport.ts @@ -54,7 +54,7 @@ namespace ts.refactor.installTypesForPackage { const newImportClause = createImportClause(name, /*namedBindings*/ undefined); const newImportStatement = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, newImportClause, moduleSpecifier); return { - edits: textChanges.ChangeTracker.with(context, t => t.replaceNode(file, importStatement, newImportStatement)), + edits: textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(file, importStatement, newImportStatement)), renameFilename: undefined, renameLocation: undefined, }; diff --git a/src/services/services.ts b/src/services/services.ts index 4236416fbb3..1692da9872b 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1887,12 +1887,11 @@ namespace ts { synchronizeHostData(); const sourceFile = getValidSourceFile(fileName); const span = createTextSpanFromBounds(start, end); - const newLineCharacter = getNewLineOrDefaultFromHost(host); const formatContext = formatting.getFormatContext(formatOptions); return flatMap(deduplicate(errorCodes, equateValues, compareValues), errorCode => { cancellationToken.throwIfCancellationRequested(); - return codefix.getFixes({ errorCode, sourceFile, span, program, newLineCharacter, host, cancellationToken, formatContext }); + return codefix.getFixes({ errorCode, sourceFile, span, program, host, cancellationToken, formatContext }); }); } @@ -1900,10 +1899,9 @@ namespace ts { synchronizeHostData(); Debug.assert(scope.type === "file"); const sourceFile = getValidSourceFile(scope.fileName); - const newLineCharacter = getNewLineOrDefaultFromHost(host); const formatContext = formatting.getFormatContext(formatOptions); - return codefix.getAllFixes({ fixId, sourceFile, program, newLineCharacter, host, cancellationToken, formatContext }); + return codefix.getAllFixes({ fixId, sourceFile, program, host, cancellationToken, formatContext }); } function applyCodeActionCommand(action: CodeActionCommand): Promise; @@ -2134,7 +2132,6 @@ namespace ts { startPosition, endPosition, program: getProgram(), - newLineCharacter: formatOptions ? formatOptions.newLineCharacter : host.getNewLine(), host, formatContext: formatting.getFormatContext(formatOptions), cancellationToken, From db3f7c538e324be4219dab947e3734f81e07b508 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 11 Jan 2018 17:43:27 -0800 Subject: [PATCH 039/100] Update test baselines --- .../fourslash/autoFormattingOnPasting.ts | 12 ++-- tests/cases/fourslash/classInterfaceInsert.ts | 2 +- .../fourslash/codeFixAddMissingMember.ts | 2 +- .../fourslash/codeFixAddMissingMember2.ts | 2 +- .../fourslash/codeFixAddMissingMember3.ts | 2 +- .../fourslash/codeFixAddMissingMember4.ts | 4 +- .../fourslash/codeFixAddMissingMember5.ts | 4 +- .../fourslash/codeFixAddMissingMember6.ts | 4 +- .../fourslash/codeFixAddMissingMember7.ts | 4 +- .../fourslash/codeFixAddMissingMember_all.ts | 8 +-- .../codeFixAddMissingMember_all_js.ts | 10 ++-- ...ClassImplementClassFunctionVoidInferred.ts | 8 +-- ...prExtendsAbstractExpressionWithTypeArgs.ts | 4 +- ...assExtendAbstractExpressionWithTypeArgs.ts | 4 +- .../codeFixClassExtendAbstractGetterSetter.ts | 16 ++--- .../codeFixClassExtendAbstractMethod.ts | 22 +++---- .../codeFixClassExtendAbstractMethodThis.ts | 8 +-- ...stractMethodTypeParamsInstantiateNumber.ts | 8 +-- ...endAbstractMethodTypeParamsInstantiateU.ts | 8 +-- .../codeFixClassExtendAbstractMethod_all.ts | 16 ++--- .../codeFixClassExtendAbstractProperty.ts | 8 +-- .../codeFixClassExtendAbstractPropertyThis.ts | 4 +- ...FixClassExtendAbstractProtectedProperty.ts | 4 +- ...odeFixClassExtendAbstractPublicProperty.ts | 4 +- ...ImplementClassAbstractGettersAndSetters.ts | 8 +-- ...ClassImplementClassFunctionVoidInferred.ts | 8 +-- ...xClassImplementClassMultipleSignatures1.ts | 10 ++-- ...xClassImplementClassMultipleSignatures2.ts | 14 ++--- ...FixClassImplementClassPropertyModifiers.ts | 8 +-- ...FixClassImplementClassPropertyTypeQuery.ts | 4 +- .../codeFixClassImplementDeepInheritance.ts | 14 ++--- .../codeFixClassImplementDefaultClass.ts | 4 +- ...odeFixClassImplementInterfaceArrayTuple.ts | 8 +-- ...xClassImplementInterfaceClassExpression.ts | 4 +- .../codeFixClassImplementInterfaceComments.ts | 12 ++-- ...lementInterfaceComputedPropertyLiterals.ts | 18 +++--- ...aceComputedPropertyNameWellKnownSymbols.ts | 58 +++++++++---------- ...deFixClassImplementInterfaceInNamespace.ts | 8 +-- ...ssImplementInterfaceIndexSignaturesBoth.ts | 6 +- ...ImplementInterfaceIndexSignaturesNumber.ts | 4 +- ...ImplementInterfaceIndexSignaturesString.ts | 4 +- ...codeFixClassImplementInterfaceIndexType.ts | 4 +- ...mplementInterfaceInheritsAbstractMethod.ts | 8 +-- ...odeFixClassImplementInterfaceMappedType.ts | 4 +- ...ImplementInterfaceMemberNestedTypeAlias.ts | 10 ++-- ...ixClassImplementInterfaceMemberOrdering.ts | 50 ++++++++-------- ...xClassImplementInterfaceMemberTypeAlias.ts | 10 ++-- ...mentInterfaceMethodThisAndSelfReference.ts | 8 +-- ...ssImplementInterfaceMethodTypePredicate.ts | 12 ++-- ...tInterfaceMultipleMembersAndPunctuation.ts | 26 ++++----- ...assImplementInterfaceMultipleSignatures.ts | 14 ++--- ...plementInterfaceMultipleSignaturesRest1.ts | 14 ++--- ...plementInterfaceMultipleSignaturesRest2.ts | 14 ++--- ...lassImplementInterfaceNamespaceConflict.ts | 4 +- ...ClassImplementInterfaceOptionalProperty.ts | 6 +- .../codeFixClassImplementInterfaceProperty.ts | 10 ++-- ...assImplementInterfacePropertySignatures.ts | 24 ++++---- ...FixClassImplementInterfaceQualifiedName.ts | 4 +- ...mentInterfaceTypeParamInstantiateDeeply.ts | 4 +- ...mentInterfaceTypeParamInstantiateNumber.ts | 4 +- ...ImplementInterfaceTypeParamInstantiateT.ts | 4 +- ...ImplementInterfaceTypeParamInstantiateU.ts | 4 +- ...xClassImplementInterfaceTypeParamMethod.ts | 8 +-- .../codeFixClassImplementInterface_all.ts | 22 +++---- .../codeFixClassSuperMustPrecedeThisAccess.ts | 2 +- ...eFixClassSuperMustPrecedeThisAccess_all.ts | 4 +- ...deFixConstructorForDerivedNeedSuperCall.ts | 4 +- ...xConstructorForDerivedNeedSuperCall_all.ts | 8 +-- .../codeFixDisableJsDiagnosticsInFile_all.ts | 4 +- .../codeFixUndeclaredInStaticMethod.ts | 48 +++++++-------- .../fourslash/codeFixUndeclaredMethod.ts | 36 ++++++------ ...completionAfterBackslashFollowingString.ts | 2 +- ...Import_default_alreadyExistedWithRename.ts | 2 +- .../completionsImport_default_anonymous.ts | 4 +- ...letionsImport_default_didNotExistBefore.ts | 4 +- ...sImport_default_exportDefaultIdentifier.ts | 4 +- .../completionsImport_fromAmbientModule.ts | 4 +- .../completionsImport_multipleWithSameName.ts | 4 +- ...tionsImport_named_exportEqualsNamespace.ts | 4 +- .../fourslash/completionsImport_ofAlias.ts | 4 +- ...mpletionsImport_ofAlias_preferShortPath.ts | 4 +- .../fourslash/completionsImport_require.ts | 8 +-- ...tenceOnIndentionsOfChainedFunctionCalls.ts | 2 +- ...ocCommentTemplateFunctionWithParameters.ts | 4 +- .../fourslash/findReferencesAfterEdit.ts | 2 +- tests/cases/fourslash/formatEmptyBlock.ts | 2 +- .../cases/fourslash/formatTemplateLiteral.ts | 4 +- .../formattingOnDoWhileNoSemicolon.ts | 2 +- .../formattingOnNestedDoWhileByEnter.ts | 2 +- ...formattingWithEnterAfterMultilineString.ts | 2 +- .../fourslash/getOccurrencesAfterEdit.ts | 2 +- .../fourslash/importNameCodeFixReExport.ts | 4 +- .../noSmartIndentInsideMultilineString.ts | 2 +- .../shims-pp/getOccurrencesAtPosition.ts | 2 +- .../shims/getOccurrencesAtPosition.ts | 2 +- .../smartIndentArrayBindingPattern01.ts | 2 +- .../smartIndentArrayBindingPattern02.ts | 2 +- .../cases/fourslash/smartIndentDoStatement.ts | 2 +- .../cases/fourslash/smartIndentIfStatement.ts | 2 +- .../smartIndentInParenthesizedExpression01.ts | 2 +- .../smartIndentObjectBindingPattern01.ts | 2 +- .../smartIndentObjectBindingPattern02.ts | 2 +- .../cases/fourslash/smartIndentOnAccessors.ts | 4 +- .../fourslash/smartIndentOnAccessors01.ts | 4 +- .../smartIndentOnFunctionParameters.ts | 4 +- ...rtIndentOnUnclosedFunctionDeclaration01.ts | 2 +- ...rtIndentOnUnclosedFunctionDeclaration02.ts | 2 +- ...rtIndentOnUnclosedFunctionDeclaration03.ts | 2 +- ...rtIndentOnUnclosedFunctionDeclaration04.ts | 2 +- .../fourslash/smartIndentStartLineInLists.ts | 2 +- .../fourslash/smartIndentTemplateLiterals.ts | 2 +- 111 files changed, 424 insertions(+), 424 deletions(-) diff --git a/tests/cases/fourslash/autoFormattingOnPasting.ts b/tests/cases/fourslash/autoFormattingOnPasting.ts index 5c9e2c86e37..7eb08116041 100644 --- a/tests/cases/fourslash/autoFormattingOnPasting.ts +++ b/tests/cases/fourslash/autoFormattingOnPasting.ts @@ -11,12 +11,12 @@ public testMethod( ) }`); // We're missing scenarios of formatting option settings due to bug 693273 - [TypeScript] Need to improve fourslash support for formatting options. // Missing scenario ** Uncheck Tools->Options->Text Editor->TypeScript->Formatting->General->Format on paste ** -//verify.currentFileContentIs("module TestModule {\r\n\ -// class TestClass{\r\n\ -//private foo;\r\n\ -//public testMethod( )\r\n\ -//{}\r\n\ -//}\r\n\ +//verify.currentFileContentIs("module TestModule {\n\ +// class TestClass{\n\ +//private foo;\n\ +//public testMethod( )\n\ +//{}\n\ +//}\n\ //}"); // Missing scenario ** Check Tools->Options->Text Editor->TypeScript->Formatting->General->Format on paste ** verify.currentFileContentIs(`module TestModule { diff --git a/tests/cases/fourslash/classInterfaceInsert.ts b/tests/cases/fourslash/classInterfaceInsert.ts index e2610532083..d645d6eb8eb 100644 --- a/tests/cases/fourslash/classInterfaceInsert.ts +++ b/tests/cases/fourslash/classInterfaceInsert.ts @@ -12,6 +12,6 @@ verify.quickInfoAt("className", "class Sphere"); goTo.marker('interfaceGoesHere'); -edit.insert("\r\ninterface Surface {\r\n reflect: () => number;\r\n}\r\n"); +edit.insert("\ninterface Surface {\n reflect: () => number;\n}\n"); verify.quickInfoAt("className", "class Sphere"); diff --git a/tests/cases/fourslash/codeFixAddMissingMember.ts b/tests/cases/fourslash/codeFixAddMissingMember.ts index 563cc29f4ea..8c337c30318 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember.ts @@ -11,7 +11,7 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newFileContent: `class C { - foo: number;\r + foo: number; method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember2.ts b/tests/cases/fourslash/codeFixAddMissingMember2.ts index 06e111e63c7..e8a28810a60 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember2.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember2.ts @@ -11,7 +11,7 @@ verify.codeFix({ index: 1, // TODO: GH#18445 newFileContent: `class C { - [x: string]: number;\r + [x: string]: number; method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember3.ts b/tests/cases/fourslash/codeFixAddMissingMember3.ts index adc099eff74..8367e6918cc 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember3.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember3.ts @@ -11,7 +11,7 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newFileContent: `class C { - static foo: number;\r + static foo: number; static method() { this.foo = 10; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember4.ts b/tests/cases/fourslash/codeFixAddMissingMember4.ts index 97a17959d26..5f84b28cde3 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember4.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember4.ts @@ -17,8 +17,8 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newFileContent: `class C { - constructor() {\r - this.foo = undefined;\r + constructor() { + this.foo = undefined; } method() { this.foo === 10; diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index a15b564c786..727253f0b9d 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -18,7 +18,7 @@ verify.codeFix({ static method() { ()=>{ this.foo === 10 }; } -}\r -C.foo = undefined;\r +} +C.foo = undefined; ` }); diff --git a/tests/cases/fourslash/codeFixAddMissingMember6.ts b/tests/cases/fourslash/codeFixAddMissingMember6.ts index a04a14c9ce5..f418f2c3ebc 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember6.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember6.ts @@ -15,8 +15,8 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newFileContent: `class C { - constructor() {\r - this.foo = undefined;\r + constructor() { + this.foo = undefined; } prop = ()=>{ this.foo === 10 }; }` diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 0655898218b..8a475ebaf9c 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -14,7 +14,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `class C { static p = ()=>{ this.foo === 10 }; -}\r -C.foo = undefined;\r +} +C.foo = undefined; ` }); diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all.ts b/tests/cases/fourslash/codeFixAddMissingMember_all.ts index e7edad47227..9b9618ecaeb 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all.ts @@ -13,10 +13,10 @@ verify.codeFixAll({ newFileContent: // TODO: GH#18445 `class C { - x: number;\r - y(): any {\r - throw new Error("Method not implemented.");\r - }\r + x: number; + y(): any { + throw new Error("Method not implemented."); + } method() { this.x = 0; this.y(); diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts index 1bc0d03c211..67a91fd8b93 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts @@ -18,11 +18,11 @@ verify.codeFixAll({ newFileContent: // TODO: GH#18445 `class C { - y() {\r - throw new Error("Method not implemented.");\r - }\r - constructor() {\r - this.x = undefined;\r + y() { + throw new Error("Method not implemented."); + } + constructor() { + this.x = undefined; } method() { this.x; diff --git a/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts b/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts index 68743288c71..9fa45fde8dc 100644 --- a/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassExprClassImplementClassFunctionVoidInferred.ts @@ -12,9 +12,9 @@ verify.codeFix({ `class A { f() {} } -let B = class implements A {\r - f(): void {\r - throw new Error("Method not implemented.");\r - }\r +let B = class implements A { + f(): void { + throw new Error("Method not implemented."); + } }` }); diff --git a/tests/cases/fourslash/codeFixClassExprExtendsAbstractExpressionWithTypeArgs.ts b/tests/cases/fourslash/codeFixClassExprExtendsAbstractExpressionWithTypeArgs.ts index 76117f05090..ef85b2c6cdf 100644 --- a/tests/cases/fourslash/codeFixClassExprExtendsAbstractExpressionWithTypeArgs.ts +++ b/tests/cases/fourslash/codeFixClassExprExtendsAbstractExpressionWithTypeArgs.ts @@ -20,7 +20,7 @@ verify.codeFix({ return C; } -let B = class extends foo("s") {\r - a: string | number;\r +let B = class extends foo("s") { + a: string | number; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractExpressionWithTypeArgs.ts b/tests/cases/fourslash/codeFixClassExtendAbstractExpressionWithTypeArgs.ts index b7557419de0..e21b7a21e26 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractExpressionWithTypeArgs.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractExpressionWithTypeArgs.ts @@ -20,7 +20,7 @@ verify.codeFix({ return C; } -class B extends foo("s") {\r - a: string | number;\r +class B extends foo("s") { + a: string | number; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts index 4949ddbf7c5..8dd0e508ce9 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractGetterSetter.ts @@ -42,13 +42,13 @@ verify.codeFix({ // Don't need to add anything in this case. abstract class B extends A {} -class C extends A {\r - a: string | number;\r - b: this;\r - c: A;\r - d: string | number;\r - e: this;\r - f: A;\r - g: string;\r +class C extends A { + a: string | number; + b: this; + c: A; + d: string | number; + e: this; + f: A; + g: string; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts index 462dd1e18ea..7098af8bc27 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod.ts @@ -22,16 +22,16 @@ verify.codeFix({ abstract foo(): number; } -class C extends A {\r - f(a: number, b: string): boolean;\r - f(a: number, b: string): this;\r - f(a: string, b: number): Function;\r - f(a: string): Function;\r - f(a: any, b?: any) {\r - throw new Error("Method not implemented.");\r - }\r - foo(): number {\r - throw new Error("Method not implemented.");\r - }\r +class C extends A { + f(a: number, b: string): boolean; + f(a: number, b: string): this; + f(a: string, b: number): Function; + f(a: string): Function; + f(a: any, b?: any) { + throw new Error("Method not implemented."); + } + foo(): number { + throw new Error("Method not implemented."); + } }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts index 7fa5b762787..f0bb90c9443 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodThis.ts @@ -14,9 +14,9 @@ verify.codeFix({ abstract f(): this; } -class C extends A {\r - f(): this {\r - throw new Error("Method not implemented.");\r - }\r +class C extends A { + f(): this { + throw new Error("Method not implemented."); + } }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts index 01411fb34cb..fdbbda75113 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateNumber.ts @@ -14,9 +14,9 @@ verify.codeFix({ abstract f(x: T): T; } -class C extends A {\r - f(x: number): number {\r - throw new Error("Method not implemented.");\r - }\r +class C extends A { + f(x: number): number { + throw new Error("Method not implemented."); + } }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts index aff612ac95d..41fe94ae634 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethodTypeParamsInstantiateU.ts @@ -14,9 +14,9 @@ verify.codeFix({ abstract f(x: T): T; } -class C extends A {\r - f(x: U): U {\r - throw new Error("Method not implemented.");\r - }\r +class C extends A { + f(x: U): U { + throw new Error("Method not implemented."); + } }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts index ba6afb9ac7a..aa28f301cc8 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts @@ -13,14 +13,14 @@ verify.codeFixAll({ `abstract class A { abstract m(): void; } -class B extends A {\r - m(): void {\r - throw new Error("Method not implemented.");\r - }\r +class B extends A { + m(): void { + throw new Error("Method not implemented."); + } } -class C extends A {\r - m(): void {\r - throw new Error("Method not implemented.");\r - }\r +class C extends A { + m(): void { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts index 6fd77e90385..e5811016e8c 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProperty.ts @@ -18,9 +18,9 @@ verify.codeFix({ abstract z: A; } -class C extends A {\r - x: number;\r - y: this;\r - z: A;\r +class C extends A { + x: number; + y: this; + z: A; }` }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts index 9531c3b7533..c1184c083a5 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts @@ -14,7 +14,7 @@ verify.codeFix({ abstract x: this; } -class C extends A {\r - x: this;\r +class C extends A { + x: this; }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts index 63a3ed99f67..e2472318e10 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts @@ -14,7 +14,7 @@ verify.codeFix({ protected abstract x: number; } -class C extends A {\r - protected x: number;\r +class C extends A { + protected x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts index c8696b1ad6c..bb9ed1ec9c5 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts @@ -14,7 +14,7 @@ verify.codeFix({ public abstract x: number; } -class C extends A {\r - public x: number;\r +class C extends A { + public x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts b/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts index f6fac956c5b..ee28448f06e 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts @@ -28,9 +28,9 @@ verify.codeFix({ abstract set c(arg: number | string); } -class C implements A {\r - a: string;\r - b: number;\r - c: string | number;\r +class C implements A { + a: string; + b: number; + c: string | number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts index dbc0689644f..8b749145ec2 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts @@ -14,9 +14,9 @@ verify.codeFix({ f() {} } -class B implements A {\r - f(): void {\r - throw new Error("Method not implemented.");\r - }\r +class B implements A { + f(): void { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts index 8c563d4dbb7..a2492787bf7 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts @@ -14,10 +14,10 @@ verify.codeFix({ method(a: number, b: string): boolean; method(a: string | number, b?: string | number): boolean | Function { return true; } } -class C implements A {\r - method(a: number, b: string): boolean;\r - method(a: string | number, b?: string | number): boolean | Function {\r - throw new Error("Method not implemented.");\r - }\r +class C implements A { + method(a: number, b: string): boolean; + method(a: string | number, b?: string | number): boolean | Function { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts index de2bfab561e..83e531c9ef4 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts @@ -18,12 +18,12 @@ verify.codeFix({ method(a: string): Function; method(a: string | number, b?: string | number): boolean | Function { return true; } } -class C implements A {\r - method(a: any, b: string): boolean;\r - method(a: string, b: number): Function;\r - method(a: string): Function;\r - method(a: string | number, b?: string | number): boolean | Function {\r - throw new Error("Method not implemented.");\r - }\r +class C implements A { + method(a: any, b: string): boolean; + method(a: string, b: number): Function; + method(a: string): Function; + method(a: string | number, b?: string | number): boolean | Function { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts b/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts index f3aa6bc6c6c..59d406541a6 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts @@ -20,9 +20,9 @@ verify.codeFix({ public w: number; } -class C implements A {\r - x: number;\r - protected z: number;\r - public w: number;\r +class C implements A { + x: number; + protected z: number; + public w: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts index 0981db995bb..326ad2b547e 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts @@ -12,7 +12,7 @@ verify.codeFix({ `class A { A: typeof A; } -class D implements A {\r - A: typeof A;\r +class D implements A { + A: typeof A; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts index ae187b93572..785ffb80a08 100644 --- a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts +++ b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts @@ -75,12 +75,12 @@ class C4 extends C3 implements I0, I4, I5 { } interface I6 extends C4 {} -class C5 implements I6 {\r - e: number;\r - f: number;\r - a: number;\r - b: number;\r - d: number;\r - c: number;\r +class C5 implements I6 { + e: number; + f: number; + a: number; + b: number; + d: number; + c: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts b/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts index 53fb9378591..e2ffab5a784 100644 --- a/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts +++ b/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts @@ -8,7 +8,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `interface I { x: number; } -export default class implements I {\r - x: number;\r +export default class implements I { + x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts index b121fe54ac7..05c5ceaa6bc 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts @@ -18,9 +18,9 @@ verify.codeFix({ z: [number, string, I]; } -class C implements I {\r - x: number[];\r - y: number[];\r - z: [number, string, I];\r +class C implements I { + x: number[]; + y: number[]; + z: [number, string, I]; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts index 8b811a98d25..0340cdf180b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts @@ -9,7 +9,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `interface I { x: number; } -new class implements I {\r - x: number;\r +new class implements I { + x: number; };`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts index 98a00d43f4c..a80dcf0c4b9 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts @@ -36,11 +36,11 @@ verify.codeFix({ /**method signature prefix */foo /**open angle prefix */< /**type parameter name prefix */ X /** closing angle prefix */> /**open paren prefix */(/** parameter prefix */ a/** colon prefix */: /** parameter type prefix */ X /** close paren prefix */) /** colon prefix */: /** return type prefix */ string /** semicolon prefix */; /**close-brace prefix*/ } /**close-brace prefix*/ } -class C implements N.I {\r - /** property prefix */ a /** colon prefix */: N.E.a;\r - /** property prefix */ b /** colon prefix */: N.E;\r - /**method signature prefix */ foo /**open angle prefix */(a: X): string {\r - throw new Error("Method not implemented.");\r - }\r +class C implements N.I { + /** property prefix */ a /** colon prefix */: N.E.a; + /** property prefix */ b /** colon prefix */: N.E; + /**method signature prefix */ foo /**open angle prefix */(a: X): string { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts index 6bbe5f4efd1..4e999d38426 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts @@ -20,14 +20,14 @@ verify.codeFix({ [2]: boolean; } -class C implements I {\r - ["foo"](o: any): boolean {\r - throw new Error("Method not implemented.");\r - }\r - ["x"]: boolean;\r - [1](): string {\r - throw new Error("Method not implemented.");\r - }\r - [2]: boolean;\r +class C implements I { + ["foo"](o: any): boolean { + throw new Error("Method not implemented."); + } + ["x"]: boolean; + [1](): string { + throw new Error("Method not implemented."); + } + [2]: boolean; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts index ab8b7ddfebb..be9f6ddbbec 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts @@ -38,34 +38,34 @@ verify.codeFix({ [Symbol.toStringTag]: string; [Symbol.unscopables]: any; } -class C implements I {\r - [Symbol.hasInstance](o: any): boolean {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.isConcatSpreadable]: boolean;\r - [Symbol.iterator]() {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.match]: boolean;\r - [Symbol.replace](...args: {}) {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.search](str: string): number {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.species](): number {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.split](str: string, limit?: number): {} {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.toPrimitive](hint: "number"): number;\r - [Symbol.toPrimitive](hint: "default"): number;\r - [Symbol.toPrimitive](hint: "string"): string;\r - [Symbol.toPrimitive](hint: any) {\r - throw new Error("Method not implemented.");\r - }\r - [Symbol.toStringTag]: string\;\r - [Symbol.unscopables]: any;\r +class C implements I { + [Symbol.hasInstance](o: any): boolean { + throw new Error("Method not implemented."); + } + [Symbol.isConcatSpreadable]: boolean; + [Symbol.iterator]() { + throw new Error("Method not implemented."); + } + [Symbol.match]: boolean; + [Symbol.replace](...args: {}) { + throw new Error("Method not implemented."); + } + [Symbol.search](str: string): number { + throw new Error("Method not implemented."); + } + [Symbol.species](): number { + throw new Error("Method not implemented."); + } + [Symbol.split](str: string, limit?: number): {} { + throw new Error("Method not implemented."); + } + [Symbol.toPrimitive](hint: "number"): number; + [Symbol.toPrimitive](hint: "default"): number; + [Symbol.toPrimitive](hint: "string"): string; + [Symbol.toPrimitive](hint: any) { + throw new Error("Method not implemented."); + } + [Symbol.toStringTag]: string\; + [Symbol.unscopables]: any; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts index 9e01e080af6..b52e4d98c38 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts @@ -24,9 +24,9 @@ interface I1 { f1(); } -class C1 implements N1.I1 {\r - f1(): string {\r - throw new Error("Method not implemented.");\r - }\r +class C1 implements N1.I1 { + f1(): string { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts index 2becd28aa4f..011bed12d8b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts @@ -17,8 +17,8 @@ verify.codeFix({ [y: string]: I; } -class C implements I {\r - [x: number]: I;\r - [y: string]: I;\r +class C implements I { + [x: number]: I; + [y: string]: I; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts index ebd0904e88a..5b4a2bb73c0 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts @@ -12,7 +12,7 @@ verify.codeFix({ `interface I { [x: number]: I; } -class C implements I {\r - [x: number]: I;\r +class C implements I { + [x: number]: I; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts index 4cd6cd0c9a3..5f9c943330b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts @@ -14,7 +14,7 @@ verify.codeFix({ [Ƚ: string]: X; } -class C implements I {\r - [Ƚ: string]: number;\r +class C implements I { + [Ƚ: string]: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts index 7e93871c552..760d15f1d8f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts @@ -12,7 +12,7 @@ verify.codeFix({ `interface I { x: keyof X; } -class C implements I {\r - x: keyof Y;\r +class C implements I { + x: keyof Y; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts index 83a2c131d97..e4e8c409165 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -16,9 +16,9 @@ abstract class C2 { abstract fA(); } interface I1 extends C1, C2 { } -class C3 implements I1 {\r - fA() {\r - throw new Error("Method not implemented.");\r - }\r +class C3 implements I1 { + fA() { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts index 5fff622f2db..86a979b23df 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts @@ -12,7 +12,7 @@ verify.codeFix({ `interface I { x: { readonly [K in keyof X]: X[K] }; } -class C implements I {\r - x: { readonly [K in keyof Y]: Y[K]; };\r +class C implements I { + x: { readonly [K in keyof Y]: Y[K]; }; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts index fc1217f1556..af4c591fd37 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts @@ -16,10 +16,10 @@ interface I { x: Either>; foo(x: Either>): void; } -class C implements I {\r - x: Either>;\r - foo(x: Either>): void {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + x: Either>; + foo(x: Either>): void { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts index 4bb20142df3..e7d98d4ee1b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts @@ -62,30 +62,30 @@ interface I { /** a nice safe prime */ 23; } -class C implements I {\r - 1: any;\r - 2: any;\r - 3: any;\r - 4: any;\r - 5: any;\r - 6: any;\r - 7: any;\r - 8: any;\r - 9: any;\r - 10: any;\r - 11: any;\r - 12: any;\r - 13: any;\r - 14: any;\r - 15: any;\r - 16: any;\r - 17: any;\r - 18: any;\r - 19: any;\r - 20: any;\r - 21: any;\r - 22: any;\r - /** a nice safe prime */\r - 23: any;\r +class C implements I { + 1: any; + 2: any; + 3: any; + 4: any; + 5: any; + 6: any; + 7: any; + 8: any; + 9: any; + 10: any; + 11: any; + 12: any; + 13: any; + 14: any; + 15: any; + 16: any; + 17: any; + 18: any; + 19: any; + 20: any; + 21: any; + 22: any; + /** a nice safe prime */ + 23: any; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts index 23f1c79054e..87fda6bcd11 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -10,10 +10,10 @@ verify.codeFix({ newFileContent: `type MyType = [string, number]; interface I { x: MyType; test(a: MyType): void; } -class C implements I {\r - x: [string, number];\r - test(a: [string, number]): void {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + x: [string, number]; + test(a: [string, number]): void { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts index 41c8f2254db..c382b9f6858 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts @@ -14,9 +14,9 @@ verify.codeFix({ f(x: number, y: this): I } -class C implements I {\r - f(x: number, y: this): I {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + f(x: number, y: this): I { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts index ce1dfead0f3..9c916e3f4c6 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts @@ -16,11 +16,11 @@ verify.codeFix({ f(): this is I; } -class C implements I {\r - f(i: any): i is I;\r - f(): this is I;\r - f(i?: any) {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + f(i: any): i is I; + f(): this is I; + f(i?: any) { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts index afad257a699..9b7a91891a7 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts @@ -24,18 +24,18 @@ verify.codeFix({ h(); } -class C1 implements I1 {\r - x: number;\r - y: number;\r - z: number;\r - f() {\r - throw new Error("Method not implemented.");\r - }\r - g() {\r - throw new Error("Method not implemented.");\r - }\r - h() {\r - throw new Error("Method not implemented.");\r - }\r +class C1 implements I1 { + x: number; + y: number; + z: number; + f() { + throw new Error("Method not implemented."); + } + g() { + throw new Error("Method not implemented."); + } + h() { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts index b8167ccf35b..05b08d6f570 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts @@ -18,12 +18,12 @@ verify.codeFix({ method(a: string): Function; } -class C implements I {\r - method(a: number, b: string): boolean;\r - method(a: string, b: number): Function;\r - method(a: string): Function;\r - method(a: any, b?: any) {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + method(a: number, b: string): boolean; + method(a: string, b: number): Function; + method(a: string): Function; + method(a: any, b?: any) { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts index ee64c1be6bb..91f142db5dd 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts @@ -18,12 +18,12 @@ verify.codeFix({ method(a: string): Function; } -class C implements I {\r - method(a: number, ...b: string[]): boolean;\r - method(a: string, ...b: number[]): Function;\r - method(a: string): Function;\r - method(a: any, ...b?: any[]) {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + method(a: number, ...b: string[]): boolean; + method(a: string, ...b: number[]): Function; + method(a: string): Function; + method(a: any, ...b?: any[]) { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts index 9b4e471a5d8..ae57831f0ea 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts @@ -18,12 +18,12 @@ verify.codeFix({ method(a: string): Function; } -class C implements I {\r - method(a: number, ...b: string[]): boolean;\r - method(a: string, b: number): Function;\r - method(a: string): Function;\r - method(a: any, b?: any, ...rest?: any[]) {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + method(a: number, ...b: string[]): boolean; + method(a: string, b: number): Function; + method(a: string): Function; + method(a: any, b?: any, ...rest?: any[]) { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts index ee9808603cf..bca3c7b5fe2 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts @@ -18,7 +18,7 @@ verify.codeFix({ interface I1 { f1(); } -class C1 implements N1.I1 {\r - x: number;\r +class C1 implements N1.I1 { + x: number; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts index ea4f5dbdd91..215d11f1d94 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts @@ -14,8 +14,8 @@ verify.codeFix({ name: string; birthday?: string; } -class Person implements IPerson {\r - name: string;\r - birthday?: string;\r +class Person implements IPerson { + name: string; + birthday?: string; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts index f8fb576800f..db9bf753ef7 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts @@ -22,10 +22,10 @@ interface I { z: symbol; w: object; } -class C implements I {\r - x: E;\r - y: E.a;\r - z: symbol;\r - w: object;\r +class C implements I { + x: E; + y: E.a; + z: symbol; + w: object; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts index 98a0f374ae7..37a35470d53 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts @@ -40,17 +40,17 @@ verify.codeFix({ a9: { (b9: number, c9: string): number; [d9: number]: I }; a10: { (b10: number, c10: string): number; [d10: string]: I }; } -class C implements I {\r - a0: {};\r - a1: (b1: number, c1: string) => number;\r - a2: (b2: number, c2: string) => number;\r - a3: { (b3: number, c3: string): number; x: number; };\r - a4: new (b1: number, c1: string) => number;\r - a5: new (b2: number, c2: string) => number;\r - a6: { new(b3: number, c3: string): number; x: number; };\r - a7: { foo(b7: number, c7: string): number; };\r - a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number; };\r - a9: { (b9: number, c9: string): number;[d9: number]: I; };\r - a10: { (b10: number, c10: string): number;[d10: string]: I; };\r +class C implements I { + a0: {}; + a1: (b1: number, c1: string) => number; + a2: (b2: number, c2: string) => number; + a3: { (b3: number, c3: string): number; x: number; }; + a4: new (b1: number, c1: string) => number; + a5: new (b2: number, c2: string) => number; + a6: { new(b3: number, c3: string): number; x: number; }; + a7: { foo(b7: number, c7: string): number; }; + a8: { (b81: number, c81: string): number; new(b82: number, c82: string): number; }; + a9: { (b9: number, c9: string): number;[d9: number]: I; }; + a10: { (b10: number, c10: string): number;[d10: string]: I; }; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts index c8e88bced9f..9ca2894741c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts @@ -12,7 +12,7 @@ verify.codeFix({ `namespace N { export interface I { y: I; } } -class C1 implements N.I {\r - y: N.I;\r +class C1 implements N.I { + y: N.I; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts index 4b50cf2e69b..486bea3098a 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -12,7 +12,7 @@ verify.codeFix({ `interface I { x: { y: T, z: T[] }; } -class C implements I {\r - x: { y: number; z: number[]; };\r +class C implements I { + x: { y: number; z: number[]; }; }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts index 6df183d3e44..dde7bc4fc09 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts @@ -8,7 +8,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `interface I { x: T; } -class C implements I {\r - x: number;\r +class C implements I { + x: number; }` }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts index 7be7d21f1e4..4a796bcefc1 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts @@ -8,7 +8,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `interface I { x: T; } -class C implements I {\r - x: T;\r +class C implements I { + x: T; }` }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts index ac4cad80220..efe5295d3b3 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts @@ -8,7 +8,7 @@ verify.codeFix({ // TODO: GH#18445 newFileContent: `interface I { x: T; } -class C implements I {\r - x: U;\r +class C implements I { + x: U; }` }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts index 6a93c84ecf0..5596b091754 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts @@ -12,9 +12,9 @@ verify.codeFix({ `interface I { f(x: T); } -class C implements I {\r - f(x: T) {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I { + f(x: T) { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassImplementInterface_all.ts b/tests/cases/fourslash/codeFixClassImplementInterface_all.ts index fb3672a7949..641f355be32 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterface_all.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterface_all.ts @@ -11,17 +11,17 @@ verify.codeFixAll({ newFileContent: `interface I { i(): void; } interface J { j(): void; } -class C implements I, J {\r - i(): void {\r - throw new Error("Method not implemented.");\r - }\r - j(): void {\r - throw new Error("Method not implemented.");\r - }\r +class C implements I, J { + i(): void { + throw new Error("Method not implemented."); + } + j(): void { + throw new Error("Method not implemented."); + } } -class D implements J {\r - j(): void {\r - throw new Error("Method not implemented.");\r - }\r +class D implements J { + j(): void { + throw new Error("Method not implemented."); + } }`, }); diff --git a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts index a4db2c909fd..ab0860a4caa 100644 --- a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts +++ b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts @@ -11,6 +11,6 @@ ////} // TODO: GH#18445 verify.rangeAfterCodeFix(` - super();\r + super(); this.a = 12; `, /*includeWhiteSpace*/ true); diff --git a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess_all.ts b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess_all.ts index 0b12fb4b03d..96f3677fda7 100644 --- a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess_all.ts +++ b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess_all.ts @@ -18,14 +18,14 @@ verify.codeFixAll({ fixId: "classSuperMustPrecedeThisAccess", newFileContent: `class C extends Object { constructor() { - super();\r + super(); this; this; } } class D extends Object { constructor() { - super();\r + super(); this; } }`, diff --git a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts index 402d4196360..030463345f3 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts @@ -13,8 +13,8 @@ verify.codeFix({ `class Base{ } class C extends Base{ - constructor() {\r - super();\r + constructor() { + super(); } }`, }); diff --git a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts index 8aa0e2c6ade..60e6d1d4599 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts @@ -11,13 +11,13 @@ verify.codeFixAll({ fixId: "constructorForDerivedNeedSuperCall", // TODO: GH#18445 newFileContent: `class C extends Object { - constructor() {\r - super();\r + constructor() { + super(); } } class D extends Object { - constructor() {\r - super();\r + constructor() { + super(); } }`, }); diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts index 527ecb66848..9c3785cf2ae 100644 --- a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts +++ b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts @@ -13,8 +13,8 @@ verify.codeFixAll({ fixId: "disableJsDiagnostics", newFileContent: `let x = ""; -// @ts-ignore\r +// @ts-ignore x = 1; -// @ts-ignore\r +// @ts-ignore x = true;`, }); diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index b1fac4e0f12..0a978fab222 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -14,9 +14,9 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newRangeContent: ` - static m1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + static m1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } `, }); @@ -24,12 +24,12 @@ verify.codeFix({ description: "Declare static method 'm2'", index: 0, newRangeContent: ` - static m2(arg0: any, arg1: any): any {\r - throw new Error("Method not implemented.");\r - }\r - static m1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + static m2(arg0: any, arg1: any): any { + throw new Error("Method not implemented."); + } + static m1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } `, }); @@ -37,13 +37,13 @@ verify.codeFix({ description: "Declare static property 'prop1'", index: 0, newRangeContent: ` - static prop1: number;\r - static m2(arg0: any, arg1: any): any {\r - throw new Error("Method not implemented.");\r - }\r - static m1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + static prop1: number; + static m2(arg0: any, arg1: any): any { + throw new Error("Method not implemented."); + } + static m1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } `, }); @@ -51,13 +51,13 @@ verify.codeFix({ description: "Declare static property 'prop2'", index: 0, newRangeContent: ` - static prop2: string;\r - static prop1: number;\r - static m2(arg0: any, arg1: any): any {\r - throw new Error("Method not implemented.");\r - }\r - static m1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + static prop2: string; + static prop1: number; + static m2(arg0: any, arg1: any): any { + throw new Error("Method not implemented."); + } + static m1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } `, }); diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts index 2f291490b14..bffa9fb0897 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -15,9 +15,9 @@ verify.codeFix({ index: 0, // TODO: GH#18445 newRangeContent: ` - foo1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + foo1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } `, }); @@ -25,12 +25,12 @@ verify.codeFix({ description: "Declare method 'foo2'", index: 0, newRangeContent: ` - foo2(): any {\r - throw new Error("Method not implemented.");\r - }\r - foo1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + foo2(): any { + throw new Error("Method not implemented."); + } + foo1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } ` }); @@ -38,14 +38,14 @@ verify.codeFix({ description: "Declare method 'foo3'", index: 0, newRangeContent:` - foo3(): any {\r - throw new Error("Method not implemented.");\r - }\r - foo2(): any {\r - throw new Error("Method not implemented.");\r - }\r - foo1(arg0: any, arg1: any, arg2: any): any {\r - throw new Error("Method not implemented.");\r - }\r + foo3(): any { + throw new Error("Method not implemented."); + } + foo2(): any { + throw new Error("Method not implemented."); + } + foo1(arg0: any, arg1: any, arg2: any): any { + throw new Error("Method not implemented."); + } ` }); diff --git a/tests/cases/fourslash/completionAfterBackslashFollowingString.ts b/tests/cases/fourslash/completionAfterBackslashFollowingString.ts index e8eeeb571b6..2e8587ffd1f 100644 --- a/tests/cases/fourslash/completionAfterBackslashFollowingString.ts +++ b/tests/cases/fourslash/completionAfterBackslashFollowingString.ts @@ -1,6 +1,6 @@ /// -////Harness.newLine = "\r"\n/**/ +////Harness.newLine = ""\n/**/ goTo.marker(); verify.not.completionListIsEmpty(); \ No newline at end of file diff --git a/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts index 09c0e74002b..0c3df5d0f23 100644 --- a/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts +++ b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts @@ -19,6 +19,6 @@ verify.applyCodeActionFromCompletion("", { description: `Import 'foo' from module "./a"`, // TODO: GH#18445 newFileContent: `import f_o_o from "./a"; -import foo from "./a";\r +import foo from "./a"; f;`, }); diff --git a/tests/cases/fourslash/completionsImport_default_anonymous.ts b/tests/cases/fourslash/completionsImport_default_anonymous.ts index 35489c1110b..617e3c5f669 100644 --- a/tests/cases/fourslash/completionsImport_default_anonymous.ts +++ b/tests/cases/fourslash/completionsImport_default_anonymous.ts @@ -23,8 +23,8 @@ verify.applyCodeActionFromCompletion("1", { source: "/src/foo-bar", description: `Import 'fooBar' from module "./foo-bar"`, // TODO: GH#18445 - newFileContent: `import fooBar from "./foo-bar";\r -\r + newFileContent: `import fooBar from "./foo-bar"; + def fooB`, }); diff --git a/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts b/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts index 558be4bee03..0daeabf1ec7 100644 --- a/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts +++ b/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts @@ -19,7 +19,7 @@ verify.applyCodeActionFromCompletion("", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import foo from "./a";\r -\r + newFileContent: `import foo from "./a"; + f;`, }); diff --git a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts index 7549c2e001b..2b3056313e8 100644 --- a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts +++ b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts @@ -20,7 +20,7 @@ verify.applyCodeActionFromCompletion("", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import foo from "./a";\r -\r + newFileContent: `import foo from "./a"; + f;`, }); diff --git a/tests/cases/fourslash/completionsImport_fromAmbientModule.ts b/tests/cases/fourslash/completionsImport_fromAmbientModule.ts index db9b586eb42..90806f802ee 100644 --- a/tests/cases/fourslash/completionsImport_fromAmbientModule.ts +++ b/tests/cases/fourslash/completionsImport_fromAmbientModule.ts @@ -13,7 +13,7 @@ verify.applyCodeActionFromCompletion("", { source: "m", description: `Import 'x' from module "m"`, // TODO: GH#18445 - newFileContent: `import { x } from "m";\r -\r + newFileContent: `import { x } from "m"; + `, }); diff --git a/tests/cases/fourslash/completionsImport_multipleWithSameName.ts b/tests/cases/fourslash/completionsImport_multipleWithSameName.ts index d85c04ce9f8..e2bc53a94bf 100644 --- a/tests/cases/fourslash/completionsImport_multipleWithSameName.ts +++ b/tests/cases/fourslash/completionsImport_multipleWithSameName.ts @@ -24,7 +24,7 @@ verify.applyCodeActionFromCompletion("", { source: "/b", description: `Import 'foo' from module "./b"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./b";\r -\r + newFileContent: `import { foo } from "./b"; + fo`, }); diff --git a/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts b/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts index b88ce9cae0d..eb8e86194fe 100644 --- a/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts +++ b/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts @@ -20,7 +20,7 @@ verify.applyCodeActionFromCompletion("", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./a";\r -\r + newFileContent: `import { foo } from "./a"; + f;`, }); diff --git a/tests/cases/fourslash/completionsImport_ofAlias.ts b/tests/cases/fourslash/completionsImport_ofAlias.ts index 5808e6c9f1a..40889e14033 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias.ts @@ -30,7 +30,7 @@ verify.applyCodeActionFromCompletion("", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./a";\r -\r + newFileContent: `import { foo } from "./a"; + fo`, }); diff --git a/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts b/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts index b837b759b53..23938746d25 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts @@ -25,7 +25,7 @@ verify.applyCodeActionFromCompletion("", { source: "/foo/lib/foo", description: `Import 'foo' from module "./foo"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./foo";\r -\r + newFileContent: `import { foo } from "./foo"; + fo`, }); diff --git a/tests/cases/fourslash/completionsImport_require.ts b/tests/cases/fourslash/completionsImport_require.ts index 2355cd06c33..5a0839bfe52 100644 --- a/tests/cases/fourslash/completionsImport_require.ts +++ b/tests/cases/fourslash/completionsImport_require.ts @@ -24,8 +24,8 @@ verify.applyCodeActionFromCompletion("b", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./a";\r -\r + newFileContent: `import { foo } from "./a"; + const a = require("./a"); fo`, }); @@ -41,8 +41,8 @@ verify.applyCodeActionFromCompletion("c", { source: "/a", description: `Import 'foo' from module "./a"`, // TODO: GH#18445 - newFileContent: `import { foo } from "./a";\r -\r + newFileContent: `import { foo } from "./a"; + const a = import("./a"); fo`, }); diff --git a/tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts b/tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts index ec2ae9b3c22..feead97486b 100644 --- a/tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts +++ b/tests/cases/fourslash/consistenceOnIndentionsOfChainedFunctionCalls.ts @@ -15,7 +15,7 @@ ////}); goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); goTo.marker("0"); // Won't-fixed: Smart indent during chained function calls verify.indentationIs(4); \ No newline at end of file diff --git a/tests/cases/fourslash/docCommentTemplateFunctionWithParameters.ts b/tests/cases/fourslash/docCommentTemplateFunctionWithParameters.ts index b1955d98417..50cebb527ce 100644 --- a/tests/cases/fourslash/docCommentTemplateFunctionWithParameters.ts +++ b/tests/cases/fourslash/docCommentTemplateFunctionWithParameters.ts @@ -5,8 +5,8 @@ //// /*1*/ //// function foo(x: number, y: string): boolean {} -const noIndentScaffolding = "/**\r\n * \r\n * @param x\r\n * @param y\r\n */"; -const oneIndentScaffolding = "/**\r\n * \r\n * @param x\r\n * @param y\r\n */"; +const noIndentScaffolding = "/**\n * \n * @param x\n * @param y\n */"; +const oneIndentScaffolding = "/**\n * \n * @param x\n * @param y\n */"; const noIndentOffset = 8; const oneIndentOffset = noIndentOffset + 4; diff --git a/tests/cases/fourslash/findReferencesAfterEdit.ts b/tests/cases/fourslash/findReferencesAfterEdit.ts index 2d57e78c637..599be2255a3 100644 --- a/tests/cases/fourslash/findReferencesAfterEdit.ts +++ b/tests/cases/fourslash/findReferencesAfterEdit.ts @@ -15,6 +15,6 @@ verify.singleReferenceGroup("(property) A.foo: string"); goTo.marker(""); -edit.insert("\r\n"); +edit.insert("\n"); verify.singleReferenceGroup("(property) A.foo: string"); diff --git a/tests/cases/fourslash/formatEmptyBlock.ts b/tests/cases/fourslash/formatEmptyBlock.ts index 565dbc6257c..0521ab6c816 100644 --- a/tests/cases/fourslash/formatEmptyBlock.ts +++ b/tests/cases/fourslash/formatEmptyBlock.ts @@ -3,6 +3,6 @@ ////{} goTo.eof(); -edit.insert("\r\n"); +edit.insert("\n"); goTo.bof(); verify.currentLineContentIs("{ }"); \ No newline at end of file diff --git a/tests/cases/fourslash/formatTemplateLiteral.ts b/tests/cases/fourslash/formatTemplateLiteral.ts index a10f8dba8f2..6ec1ec3082e 100644 --- a/tests/cases/fourslash/formatTemplateLiteral.ts +++ b/tests/cases/fourslash/formatTemplateLiteral.ts @@ -15,10 +15,10 @@ goTo.marker("1"); -edit.insert("\r\n"); // edit will trigger formatting - should succeeed +edit.insert("\n"); // edit will trigger formatting - should succeeed goTo.marker("2"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(0); verify.currentLineContentIs("3`;") diff --git a/tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts b/tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts index 848f9943925..8304415ebfa 100644 --- a/tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts +++ b/tests/cases/fourslash/formattingOnDoWhileNoSemicolon.ts @@ -5,7 +5,7 @@ /////*4*/ i -= 2 /////*5*/ }/*1*/while (1 !== 1) goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); verify.currentLineContentIs("while (1 !== 1)"); goTo.marker("2"); verify.currentLineContentIs("do {"); diff --git a/tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts b/tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts index e35f4a333b9..2b5dc9a3a5c 100644 --- a/tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts +++ b/tests/cases/fourslash/formattingOnNestedDoWhileByEnter.ts @@ -7,7 +7,7 @@ /////*6*/}while(a!==b) /////*7*/}while(a!==b) goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); verify.currentLineContentIs(" {"); goTo.marker("2"); verify.currentLineContentIs("do{"); diff --git a/tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts b/tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts index e64c7be0492..40fc4a5934b 100644 --- a/tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts +++ b/tests/cases/fourslash/formattingWithEnterAfterMultilineString.ts @@ -8,7 +8,7 @@ ////} goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); // We actually need to verify smart (virtual) identation here rather than actual identation. Fourslash support is required. verify.indentationIs(8); goTo.marker("2"); diff --git a/tests/cases/fourslash/getOccurrencesAfterEdit.ts b/tests/cases/fourslash/getOccurrencesAfterEdit.ts index 0654cc3962c..5514e22dce9 100644 --- a/tests/cases/fourslash/getOccurrencesAfterEdit.ts +++ b/tests/cases/fourslash/getOccurrencesAfterEdit.ts @@ -12,7 +12,7 @@ goTo.marker("1"); verify.occurrencesAtPositionCount(2); goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); goTo.marker("1"); verify.occurrencesAtPositionCount(2); \ No newline at end of file diff --git a/tests/cases/fourslash/importNameCodeFixReExport.ts b/tests/cases/fourslash/importNameCodeFixReExport.ts index eb1c1a91343..e6e5f21f4ac 100644 --- a/tests/cases/fourslash/importNameCodeFixReExport.ts +++ b/tests/cases/fourslash/importNameCodeFixReExport.ts @@ -11,7 +11,7 @@ goTo.file("/b.ts"); // TODO:GH#18445 -verify.rangeAfterCodeFix(`import { x } from "./a";\r -\r +verify.rangeAfterCodeFix(`import { x } from "./a"; + export { x } from "./a"; x;`, /*includeWhiteSpace*/ true); diff --git a/tests/cases/fourslash/noSmartIndentInsideMultilineString.ts b/tests/cases/fourslash/noSmartIndentInsideMultilineString.ts index 06c0fb6e44a..a81bcb2f241 100644 --- a/tests/cases/fourslash/noSmartIndentInsideMultilineString.ts +++ b/tests/cases/fourslash/noSmartIndentInsideMultilineString.ts @@ -7,5 +7,5 @@ ////}; goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(0); \ No newline at end of file diff --git a/tests/cases/fourslash/shims-pp/getOccurrencesAtPosition.ts b/tests/cases/fourslash/shims-pp/getOccurrencesAtPosition.ts index 0654cc3962c..5514e22dce9 100644 --- a/tests/cases/fourslash/shims-pp/getOccurrencesAtPosition.ts +++ b/tests/cases/fourslash/shims-pp/getOccurrencesAtPosition.ts @@ -12,7 +12,7 @@ goTo.marker("1"); verify.occurrencesAtPositionCount(2); goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); goTo.marker("1"); verify.occurrencesAtPositionCount(2); \ No newline at end of file diff --git a/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts b/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts index 0654cc3962c..5514e22dce9 100644 --- a/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts +++ b/tests/cases/fourslash/shims/getOccurrencesAtPosition.ts @@ -12,7 +12,7 @@ goTo.marker("1"); verify.occurrencesAtPositionCount(2); goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); goTo.marker("1"); verify.occurrencesAtPositionCount(2); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts b/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts index 980f2383299..c16585f452d 100644 --- a/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts +++ b/tests/cases/fourslash/smartIndentArrayBindingPattern01.ts @@ -4,7 +4,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts b/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts index b9a1da7796a..278c9ecf1ee 100644 --- a/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts +++ b/tests/cases/fourslash/smartIndentArrayBindingPattern02.ts @@ -4,7 +4,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentDoStatement.ts b/tests/cases/fourslash/smartIndentDoStatement.ts index d121344bed0..144e04567be 100644 --- a/tests/cases/fourslash/smartIndentDoStatement.ts +++ b/tests/cases/fourslash/smartIndentDoStatement.ts @@ -7,7 +7,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentIfStatement.ts b/tests/cases/fourslash/smartIndentIfStatement.ts index 5a8c5014a77..a59b374be45 100644 --- a/tests/cases/fourslash/smartIndentIfStatement.ts +++ b/tests/cases/fourslash/smartIndentIfStatement.ts @@ -9,7 +9,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts b/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts index 253f58071d9..a3bed952e95 100644 --- a/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts +++ b/tests/cases/fourslash/smartIndentInParenthesizedExpression01.ts @@ -4,7 +4,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts b/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts index e10ef704c2c..a9bae4bbef5 100644 --- a/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts +++ b/tests/cases/fourslash/smartIndentObjectBindingPattern01.ts @@ -4,7 +4,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts b/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts index 9247f5f467b..b4c751f58a1 100644 --- a/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts +++ b/tests/cases/fourslash/smartIndentObjectBindingPattern02.ts @@ -4,7 +4,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentOnAccessors.ts b/tests/cases/fourslash/smartIndentOnAccessors.ts index a7972b1f48b..6176163a5fa 100644 --- a/tests/cases/fourslash/smartIndentOnAccessors.ts +++ b/tests/cases/fourslash/smartIndentOnAccessors.ts @@ -17,7 +17,7 @@ goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(8); goTo.marker("1"); verify.currentLineContentIs(" b,"); @@ -26,7 +26,7 @@ verify.currentLineContentIs(" //comment"); goTo.marker("3"); verify.currentLineContentIs(" c"); goTo.marker("4"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(8); goTo.marker("5"); verify.currentLineContentIs(" b,"); diff --git a/tests/cases/fourslash/smartIndentOnAccessors01.ts b/tests/cases/fourslash/smartIndentOnAccessors01.ts index a7972b1f48b..6176163a5fa 100644 --- a/tests/cases/fourslash/smartIndentOnAccessors01.ts +++ b/tests/cases/fourslash/smartIndentOnAccessors01.ts @@ -17,7 +17,7 @@ goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(8); goTo.marker("1"); verify.currentLineContentIs(" b,"); @@ -26,7 +26,7 @@ verify.currentLineContentIs(" //comment"); goTo.marker("3"); verify.currentLineContentIs(" c"); goTo.marker("4"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(8); goTo.marker("5"); verify.currentLineContentIs(" b,"); diff --git a/tests/cases/fourslash/smartIndentOnFunctionParameters.ts b/tests/cases/fourslash/smartIndentOnFunctionParameters.ts index 8823bf46917..1bc42fe2a67 100644 --- a/tests/cases/fourslash/smartIndentOnFunctionParameters.ts +++ b/tests/cases/fourslash/smartIndentOnFunctionParameters.ts @@ -12,7 +12,7 @@ //// 2/*7*/ ////] goTo.marker("0"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(4); goTo.marker("2"); verify.currentLineContentIs(" b,"); @@ -21,7 +21,7 @@ verify.currentLineContentIs(" //comment"); goTo.marker("4"); verify.currentLineContentIs(" c"); goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(4); goTo.marker("5"); verify.currentLineContentIs(" //comment"); diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts index 3e08fe5975e..9ee221225e6 100644 --- a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration01.ts @@ -5,7 +5,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts index 3de8b5ec6c3..f1e2d013c40 100644 --- a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration02.ts @@ -5,7 +5,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration03.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration03.ts index 9a6c378f6ce..cb7aad0cc66 100644 --- a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration03.ts +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration03.ts @@ -5,7 +5,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts index 2cfd27f2e3b..73bd7d388c4 100644 --- a/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts +++ b/tests/cases/fourslash/smartIndentOnUnclosedFunctionDeclaration04.ts @@ -5,7 +5,7 @@ function verifyIndentationAfterNewLine(marker: string, indentation: number): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(indentation); } diff --git a/tests/cases/fourslash/smartIndentStartLineInLists.ts b/tests/cases/fourslash/smartIndentStartLineInLists.ts index 3410b4012ee..d61930ef511 100644 --- a/tests/cases/fourslash/smartIndentStartLineInLists.ts +++ b/tests/cases/fourslash/smartIndentStartLineInLists.ts @@ -4,5 +4,5 @@ ////}) goTo.marker("1"); -edit.insert("\r\n"); +edit.insert("\n"); verify.indentationIs(4); \ No newline at end of file diff --git a/tests/cases/fourslash/smartIndentTemplateLiterals.ts b/tests/cases/fourslash/smartIndentTemplateLiterals.ts index 679978a515c..38b0252a01b 100644 --- a/tests/cases/fourslash/smartIndentTemplateLiterals.ts +++ b/tests/cases/fourslash/smartIndentTemplateLiterals.ts @@ -7,7 +7,7 @@ function verifyIndentation(marker: string): void { goTo.marker(marker); - edit.insert("\r\n"); + edit.insert("\n"); verify.indentationIs(0); } verifyIndentation("1"); From 3ca1cc406a9362328ac6bd2a16e575146bb23a05 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 17 Jan 2018 15:29:41 -0800 Subject: [PATCH 040/100] Clean up TODOs for #18445 --- src/harness/fourslash.ts | 4 +--- tests/cases/fourslash/codeFixAddMissingMember.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember2.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember3.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember4.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember5.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember6.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember7.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember_all.ts | 1 - tests/cases/fourslash/codeFixAddMissingMember_all_js.ts | 1 - tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts | 1 - .../cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts | 1 - .../fourslash/codeFixClassExtendAbstractProtectedProperty.ts | 1 - .../fourslash/codeFixClassExtendAbstractPublicProperty.ts | 1 - .../codeFixClassImplementClassAbstractGettersAndSetters.ts | 1 - .../codeFixClassImplementClassFunctionVoidInferred.ts | 1 - .../codeFixClassImplementClassMultipleSignatures1.ts | 1 - .../codeFixClassImplementClassMultipleSignatures2.ts | 1 - .../fourslash/codeFixClassImplementClassPropertyModifiers.ts | 1 - .../fourslash/codeFixClassImplementClassPropertyTypeQuery.ts | 1 - tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts | 1 - tests/cases/fourslash/codeFixClassImplementDefaultClass.ts | 1 - .../fourslash/codeFixClassImplementInterfaceArrayTuple.ts | 1 - .../codeFixClassImplementInterfaceClassExpression.ts | 1 - .../cases/fourslash/codeFixClassImplementInterfaceComments.ts | 1 - .../codeFixClassImplementInterfaceComputedPropertyLiterals.ts | 1 - ...sImplementInterfaceComputedPropertyNameWellKnownSymbols.ts | 1 - .../fourslash/codeFixClassImplementInterfaceInNamespace.ts | 1 - .../codeFixClassImplementInterfaceIndexSignaturesBoth.ts | 1 - .../codeFixClassImplementInterfaceIndexSignaturesNumber.ts | 1 - .../codeFixClassImplementInterfaceIndexSignaturesString.ts | 1 - .../fourslash/codeFixClassImplementInterfaceIndexType.ts | 1 - .../codeFixClassImplementInterfaceInheritsAbstractMethod.ts | 1 - .../fourslash/codeFixClassImplementInterfaceMappedType.ts | 1 - .../codeFixClassImplementInterfaceMemberNestedTypeAlias.ts | 1 - .../fourslash/codeFixClassImplementInterfaceMemberOrdering.ts | 1 - .../codeFixClassImplementInterfaceMemberTypeAlias.ts | 1 - ...odeFixClassImplementInterfaceMethodThisAndSelfReference.ts | 1 - .../codeFixClassImplementInterfaceMethodTypePredicate.ts | 1 - ...FixClassImplementInterfaceMultipleMembersAndPunctuation.ts | 1 - .../codeFixClassImplementInterfaceMultipleSignatures.ts | 1 - .../codeFixClassImplementInterfaceMultipleSignaturesRest1.ts | 1 - .../codeFixClassImplementInterfaceMultipleSignaturesRest2.ts | 1 - .../codeFixClassImplementInterfaceNamespaceConflict.ts | 1 - .../codeFixClassImplementInterfaceOptionalProperty.ts | 1 - .../cases/fourslash/codeFixClassImplementInterfaceProperty.ts | 1 - .../codeFixClassImplementInterfacePropertySignatures.ts | 1 - .../fourslash/codeFixClassImplementInterfaceQualifiedName.ts | 1 - ...odeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts | 1 - ...odeFixClassImplementInterfaceTypeParamInstantiateNumber.ts | 1 - .../codeFixClassImplementInterfaceTypeParamInstantiateT.ts | 1 - .../codeFixClassImplementInterfaceTypeParamInstantiateU.ts | 1 - .../codeFixClassImplementInterfaceTypeParamMethod.ts | 1 - tests/cases/fourslash/codeFixClassImplementInterface_all.ts | 2 +- .../cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts | 1 - .../fourslash/codeFixConstructorForDerivedNeedSuperCall.ts | 1 - .../codeFixConstructorForDerivedNeedSuperCall_all.ts | 1 - tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts | 1 - tests/cases/fourslash/codeFixUndeclaredMethod.ts | 1 - .../completionsImport_default_alreadyExistedWithRename.ts | 1 - tests/cases/fourslash/completionsImport_default_anonymous.ts | 1 - .../fourslash/completionsImport_default_didNotExistBefore.ts | 1 - .../completionsImport_default_exportDefaultIdentifier.ts | 1 - tests/cases/fourslash/completionsImport_fromAmbientModule.ts | 1 - .../cases/fourslash/completionsImport_multipleWithSameName.ts | 1 - .../completionsImport_named_exportEqualsNamespace.ts | 1 - .../completionsImport_named_namespaceImportExists.ts | 1 - tests/cases/fourslash/completionsImport_ofAlias.ts | 1 - .../fourslash/completionsImport_ofAlias_preferShortPath.ts | 1 - tests/cases/fourslash/completionsImport_require.ts | 2 -- tests/cases/fourslash/importNameCodeFixReExport.ts | 1 - .../fourslash/server/convertFunctionToEs6Class-server.ts | 1 - 72 files changed, 2 insertions(+), 75 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index df870034270..61b2c088e14 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2568,9 +2568,7 @@ Actual: ${stringify(fullActual)}`); const originalContent = scriptInfo.content; for (const codeFix of codeFixes) { this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false); - let text = this.rangeText(ranges[0]); - // TODO:GH#18445 (remove this line to see errors in many `importNameCodeFix` tests) - text = text.replace(/\r\n/g, "\n"); + const text = this.rangeText(ranges[0]); actualTextArray.push(text); scriptInfo.updateContent(originalContent); } diff --git a/tests/cases/fourslash/codeFixAddMissingMember.ts b/tests/cases/fourslash/codeFixAddMissingMember.ts index 8c337c30318..f5bdf567b20 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Declare property 'foo'", index: 0, - // TODO: GH#18445 newFileContent: `class C { foo: number; method() { diff --git a/tests/cases/fourslash/codeFixAddMissingMember2.ts b/tests/cases/fourslash/codeFixAddMissingMember2.ts index e8a28810a60..a4b7ffb3bfe 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember2.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember2.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Add index signature for property 'foo'", index: 1, - // TODO: GH#18445 newFileContent: `class C { [x: string]: number; method() { diff --git a/tests/cases/fourslash/codeFixAddMissingMember3.ts b/tests/cases/fourslash/codeFixAddMissingMember3.ts index 8367e6918cc..82512406985 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember3.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember3.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Declare static property 'foo'", index: 0, - // TODO: GH#18445 newFileContent: `class C { static foo: number; static method() { diff --git a/tests/cases/fourslash/codeFixAddMissingMember4.ts b/tests/cases/fourslash/codeFixAddMissingMember4.ts index 5f84b28cde3..58348bca053 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember4.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember4.ts @@ -15,7 +15,6 @@ verify.codeFix({ description: "Initialize property 'foo' in the constructor", index: 0, - // TODO: GH#18445 newFileContent: `class C { constructor() { this.foo = undefined; diff --git a/tests/cases/fourslash/codeFixAddMissingMember5.ts b/tests/cases/fourslash/codeFixAddMissingMember5.ts index 727253f0b9d..64a62b8268a 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Initialize static property 'foo'", index: 0, - // TODO: GH#18445 newFileContent: `class C { static method() { ()=>{ this.foo === 10 }; diff --git a/tests/cases/fourslash/codeFixAddMissingMember6.ts b/tests/cases/fourslash/codeFixAddMissingMember6.ts index f418f2c3ebc..902cafdfb24 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember6.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember6.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Initialize property 'foo' in the constructor", index: 0, - // TODO: GH#18445 newFileContent: `class C { constructor() { this.foo = undefined; diff --git a/tests/cases/fourslash/codeFixAddMissingMember7.ts b/tests/cases/fourslash/codeFixAddMissingMember7.ts index 8a475ebaf9c..6b80901a7bb 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -11,7 +11,6 @@ verify.codeFix({ description: "Initialize static property 'foo'", index: 2, - // TODO: GH#18445 newFileContent: `class C { static p = ()=>{ this.foo === 10 }; } diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all.ts b/tests/cases/fourslash/codeFixAddMissingMember_all.ts index 9b9618ecaeb..ae5e957732b 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all.ts @@ -11,7 +11,6 @@ verify.codeFixAll({ fixId: "addMissingMember", newFileContent: - // TODO: GH#18445 `class C { x: number; y(): any { diff --git a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts index 67a91fd8b93..305b6d87c30 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts @@ -16,7 +16,6 @@ verify.codeFixAll({ fixId: "addMissingMember", newFileContent: - // TODO: GH#18445 `class C { y() { throw new Error("Method not implemented."); diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts index aa28f301cc8..9521173ad89 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts @@ -8,7 +8,6 @@ verify.codeFixAll({ fixId: "fixClassDoesntImplementInheritedAbstractMember", - // TODO: GH#18445 newFileContent: `abstract class A { abstract m(): void; diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts index c1184c083a5..06b9dddcd8a 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { abstract x: this; diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts index e2472318e10..a150dd07f8d 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { protected abstract x: number; diff --git a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts index bb9ed1ec9c5..48a023e96fa 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { public abstract x: number; diff --git a/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts b/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts index ee28448f06e..adea74627b8 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassAbstractGettersAndSetters.ts @@ -15,7 +15,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `abstract class A { private _a: string; diff --git a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts index 8b749145ec2..115517f4e6d 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { f() {} diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts index a2492787bf7..a0b59c6372c 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { method(a: number, b: string): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts index 83e531c9ef4..dcd5636428d 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures2.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { method(a: any, b: string): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts b/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts index 59d406541a6..7e7a712b60e 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyModifiers.ts @@ -11,7 +11,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `abstract class A { abstract x: number; diff --git a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts index 326ad2b547e..3c34de1e1bf 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { A: typeof A; diff --git a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts index 785ffb80a08..c9a568effbf 100644 --- a/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts +++ b/tests/cases/fourslash/codeFixClassImplementDeepInheritance.ts @@ -50,7 +50,6 @@ verify.codeFix({ description: "Implement interface 'I6'", - // TODO: GH#18445 newFileContent: `// Referenced throughout the inheritance chain. interface I0 { a: number } diff --git a/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts b/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts index e2ffab5a784..8793872ce95 100644 --- a/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts +++ b/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts @@ -5,7 +5,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: number; } export default class implements I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts index 05c5ceaa6bc..c6ad69ffb44 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceArrayTuple.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: number[]; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts index 0340cdf180b..cd9b689736d 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts @@ -6,7 +6,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: number; } new class implements I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts index a80dcf0c4b9..793ad944732 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComments.ts @@ -20,7 +20,6 @@ verify.codeFix({ description: "Implement interface 'N.I'", - // TODO: GH#18445 newFileContent: `namespace N { /**enum prefix */ diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts index 4e999d38426..daa46c6b2e9 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyLiterals.ts @@ -11,7 +11,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { ["foo"](o: any): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts index be9f6ddbbec..2bc7a738258 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceComputedPropertyNameWellKnownSymbols.ts @@ -21,7 +21,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [Symbol.hasInstance](o: any): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts index b52e4d98c38..0a28afd4b5d 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInNamespace.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Implement interface 'N1.I1'", - // TODO: GH#18445 newFileContent: `namespace N1 { export interface I1 { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts index 011bed12d8b..15fdcdab913 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [x: number]: I; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts index 5b4a2bb73c0..ede4ed4f568 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [x: number]: I; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts index 5f9c943330b..2e2c49d2700 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [Ƚ: string]: X; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts index 760d15f1d8f..2a2b4c32c1b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: keyof X; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts index e4e8c409165..516919b13df 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Implement interface 'I1'", - // TODO: GH#18445 newFileContent: `abstract class C1 { } abstract class C2 { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts index 86a979b23df..17f8ccb0bea 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: { readonly [K in keyof X]: X[K] }; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts index af4c591fd37..722fe51d0a5 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `type Either = { val: T } | Error; interface I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts index e7d98d4ee1b..9c5bd172e8f 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberOrdering.ts @@ -33,7 +33,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `/** asdf */ interface I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts index 87fda6bcd11..6fd8900d51b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -6,7 +6,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `type MyType = [string, number]; interface I { x: MyType; test(a: MyType): void; } diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts index c382b9f6858..4dfeb7cd95b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { f(x: number, y: this): I diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts index 9c916e3f4c6..45bd6096142 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts @@ -9,7 +9,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { f(i: any): i is I; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts index 9b7a91891a7..bc52629e8e9 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleMembersAndPunctuation.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Implement interface 'I1'", - // TODO: GH#18445 newFileContent: `interface I1 { x: number, diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts index 05b08d6f570..ddfc21615a3 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignatures.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { method(a: number, b: string): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts index 91f142db5dd..868a48711b2 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest1.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { method(a: number, ...b: string[]): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts index ae57831f0ea..29689f17ea7 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMultipleSignaturesRest2.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { method(a: number, ...b: string[]): boolean; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts index bca3c7b5fe2..a2b20c52f15 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceNamespaceConflict.ts @@ -10,7 +10,6 @@ verify.codeFix({ description: "Implement interface 'N1.I1'", - // TODO: GH#18445 newFileContent: `namespace N1 { export interface I1 { x: number; } diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts index 215d11f1d94..9cb6f477733 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'IPerson'", - // TODO: GH#18445 newFileContent: `interface IPerson { name: string; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts index db9bf753ef7..6fc8d8955bb 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceProperty.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `enum E { a,b,c } interface I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts index 37a35470d53..52d69b466fd 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfacePropertySignatures.ts @@ -21,7 +21,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { a0: {}; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts index 9ca2894741c..b587d937062 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'N.I'", - // TODO: GH#18445 newFileContent: `namespace N { export interface I { y: I; } diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts index 486bea3098a..cf8926ff33e 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -7,7 +7,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: { y: T, z: T[] }; diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts index dde7bc4fc09..88f0d32fe18 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts @@ -5,7 +5,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: T; } class C implements I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts index 4a796bcefc1..504b1684632 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts @@ -5,7 +5,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: T; } class C implements I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts index efe5295d3b3..c189fed4c8c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts @@ -5,7 +5,6 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { x: T; } class C implements I { diff --git a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts index 5596b091754..70db2b5908c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Implement interface 'I'", newFileContent: - // TODO: GH#18445 `interface I { f(x: T); } diff --git a/tests/cases/fourslash/codeFixClassImplementInterface_all.ts b/tests/cases/fourslash/codeFixClassImplementInterface_all.ts index 641f355be32..cc27e395ed7 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterface_all.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterface_all.ts @@ -7,7 +7,7 @@ verify.codeFixAll({ fixId: "fixClassIncorrectlyImplementsInterface", - // TODO: GH#20073 GH#18445 + // TODO: GH#20073 newFileContent: `interface I { i(): void; } interface J { j(): void; } diff --git a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts index ab0860a4caa..5d1772ba8b8 100644 --- a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts +++ b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts @@ -9,7 +9,6 @@ //// super(); //// |]} ////} -// TODO: GH#18445 verify.rangeAfterCodeFix(` super(); this.a = 12; diff --git a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts index 030463345f3..154eda0e084 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts @@ -8,7 +8,6 @@ verify.codeFix({ description: "Add missing 'super()' call", - // TODO: GH#18445 newFileContent: `class Base{ } diff --git a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts index 60e6d1d4599..3833af62de0 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts @@ -9,7 +9,6 @@ verify.codeFixAll({ fixId: "constructorForDerivedNeedSuperCall", - // TODO: GH#18445 newFileContent: `class C extends Object { constructor() { super(); diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index 0a978fab222..0bd4ac3a879 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -12,7 +12,6 @@ verify.codeFix({ description: "Declare static method 'm1'", index: 0, - // TODO: GH#18445 newRangeContent: ` static m1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); diff --git a/tests/cases/fourslash/codeFixUndeclaredMethod.ts b/tests/cases/fourslash/codeFixUndeclaredMethod.ts index bffa9fb0897..70ed58b995b 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -13,7 +13,6 @@ verify.codeFix({ description: "Declare method 'foo1'", index: 0, - // TODO: GH#18445 newRangeContent: ` foo1(arg0: any, arg1: any, arg2: any): any { throw new Error("Method not implemented."); diff --git a/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts index 0c3df5d0f23..75c45c2970b 100644 --- a/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts +++ b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts @@ -17,7 +17,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import f_o_o from "./a"; import foo from "./a"; f;`, diff --git a/tests/cases/fourslash/completionsImport_default_anonymous.ts b/tests/cases/fourslash/completionsImport_default_anonymous.ts index 617e3c5f669..ca9d9579901 100644 --- a/tests/cases/fourslash/completionsImport_default_anonymous.ts +++ b/tests/cases/fourslash/completionsImport_default_anonymous.ts @@ -22,7 +22,6 @@ verify.applyCodeActionFromCompletion("1", { name: "fooBar", source: "/src/foo-bar", description: `Import 'fooBar' from module "./foo-bar"`, - // TODO: GH#18445 newFileContent: `import fooBar from "./foo-bar"; def diff --git a/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts b/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts index 0daeabf1ec7..0e9edb4d1b2 100644 --- a/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts +++ b/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts @@ -18,7 +18,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import foo from "./a"; f;`, diff --git a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts index 2b3056313e8..45cdf71156e 100644 --- a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts +++ b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts @@ -19,7 +19,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import foo from "./a"; f;`, diff --git a/tests/cases/fourslash/completionsImport_fromAmbientModule.ts b/tests/cases/fourslash/completionsImport_fromAmbientModule.ts index 90806f802ee..43789dea095 100644 --- a/tests/cases/fourslash/completionsImport_fromAmbientModule.ts +++ b/tests/cases/fourslash/completionsImport_fromAmbientModule.ts @@ -12,7 +12,6 @@ verify.applyCodeActionFromCompletion("", { name: "x", source: "m", description: `Import 'x' from module "m"`, - // TODO: GH#18445 newFileContent: `import { x } from "m"; `, diff --git a/tests/cases/fourslash/completionsImport_multipleWithSameName.ts b/tests/cases/fourslash/completionsImport_multipleWithSameName.ts index e2bc53a94bf..da118826235 100644 --- a/tests/cases/fourslash/completionsImport_multipleWithSameName.ts +++ b/tests/cases/fourslash/completionsImport_multipleWithSameName.ts @@ -23,7 +23,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/b", description: `Import 'foo' from module "./b"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./b"; fo`, diff --git a/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts b/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts index eb8e86194fe..788c9695e45 100644 --- a/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts +++ b/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts @@ -19,7 +19,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./a"; f;`, diff --git a/tests/cases/fourslash/completionsImport_named_namespaceImportExists.ts b/tests/cases/fourslash/completionsImport_named_namespaceImportExists.ts index 112cecba74a..fddd8ae12ca 100644 --- a/tests/cases/fourslash/completionsImport_named_namespaceImportExists.ts +++ b/tests/cases/fourslash/completionsImport_named_namespaceImportExists.ts @@ -17,7 +17,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Change 'foo' to 'a.foo'`, - // TODO: GH#18445 newFileContent: `import * as a from "./a"; a.f;`, }); diff --git a/tests/cases/fourslash/completionsImport_ofAlias.ts b/tests/cases/fourslash/completionsImport_ofAlias.ts index 40889e14033..3951a93a57f 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias.ts @@ -29,7 +29,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./a"; fo`, diff --git a/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts b/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts index 23938746d25..bc66d485877 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts @@ -24,7 +24,6 @@ verify.applyCodeActionFromCompletion("", { name: "foo", source: "/foo/lib/foo", description: `Import 'foo' from module "./foo"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./foo"; fo`, diff --git a/tests/cases/fourslash/completionsImport_require.ts b/tests/cases/fourslash/completionsImport_require.ts index 5a0839bfe52..c1830b726a1 100644 --- a/tests/cases/fourslash/completionsImport_require.ts +++ b/tests/cases/fourslash/completionsImport_require.ts @@ -23,7 +23,6 @@ verify.applyCodeActionFromCompletion("b", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./a"; const a = require("./a"); @@ -40,7 +39,6 @@ verify.applyCodeActionFromCompletion("c", { name: "foo", source: "/a", description: `Import 'foo' from module "./a"`, - // TODO: GH#18445 newFileContent: `import { foo } from "./a"; const a = import("./a"); diff --git a/tests/cases/fourslash/importNameCodeFixReExport.ts b/tests/cases/fourslash/importNameCodeFixReExport.ts index e6e5f21f4ac..c77d32cc458 100644 --- a/tests/cases/fourslash/importNameCodeFixReExport.ts +++ b/tests/cases/fourslash/importNameCodeFixReExport.ts @@ -10,7 +10,6 @@ ////x;|] goTo.file("/b.ts"); -// TODO:GH#18445 verify.rangeAfterCodeFix(`import { x } from "./a"; export { x } from "./a"; diff --git a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts b/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts index e7da28a2770..0bdd44f6753 100644 --- a/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts +++ b/tests/cases/fourslash/server/convertFunctionToEs6Class-server.ts @@ -14,7 +14,6 @@ verify.applicableRefactorAvailableAtMarker('1'); // NOTE: '// Comment' should be included, but due to incorrect handling of trivia, // it's omitted right now. -// TODO: GH#18445 verify.fileAfterApplyingRefactorAtMarker('1', `class fn {\r constructor() {\r From 13bf7f9c7c392f87419e815bd31447ba168f93d7 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 17 Jan 2018 15:37:05 -0800 Subject: [PATCH 041/100] Ensure getNewLineFromContext never returns undefined --- src/services/refactorProvider.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 3d2a60e63e9..b0f5dbab324 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -21,7 +21,7 @@ namespace ts { export function getNewLineFromContext(context: RefactorOrCodeFixContext) { const formatSettings = context.formatContext.options; - return formatSettings ? formatSettings.newLineCharacter : context.host.getNewLine(); + return (formatSettings && formatSettings.newLineCharacter) || getNewLineOrDefaultFromHost(context.host); } export function toTextChangesContext(context: RefactorOrCodeFixContext): textChanges.TextChangesContext { From 3a38c8ea58c6e42779ac42c0e27fd85836d07865 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 17 Jan 2018 15:43:36 -0800 Subject: [PATCH 042/100] Replace TextChangesContext with RefactorOrCodeFixContext Thanks to @andy-ms for the suggestion! --- src/services/codeFixProvider.ts | 4 ++-- .../addMissingInvocationForDecorator.ts | 2 +- ...correctQualifiedNameToIndexedAccessType.ts | 2 +- .../codefixes/disableJsDiagnostics.ts | 4 ++-- src/services/codefixes/fixAddMissingMember.ts | 8 ++++---- .../codefixes/fixAwaitInSyncFunction.ts | 2 +- ...sDoesntImplementInheritedAbstractMember.ts | 2 +- .../fixClassIncorrectlyImplementsInterface.ts | 2 +- .../fixClassSuperMustPrecedeThisAccess.ts | 2 +- .../fixConstructorForDerivedNeedSuperCall.ts | 2 +- .../fixExtendsInterfaceBecomesImplements.ts | 2 +- .../fixForgottenThisPropertyAccess.ts | 2 +- .../codefixes/fixInvalidImportSyntax.ts | 6 +++--- src/services/codefixes/fixSpelling.ts | 2 +- src/services/codefixes/fixUnusedIdentifier.ts | 4 ++-- src/services/codefixes/importFixes.ts | 18 +++++++++--------- src/services/refactorProvider.ts | 19 +------------------ .../refactors/annotateWithTypeFromJSDoc.ts | 4 ++-- .../refactors/convertFunctionToEs6Class.ts | 2 +- src/services/refactors/convertToEs6Module.ts | 2 +- src/services/refactors/extractSymbol.ts | 4 ++-- src/services/refactors/useDefaultImport.ts | 2 +- src/services/textChanges.ts | 9 +++++++-- 23 files changed, 47 insertions(+), 59 deletions(-) diff --git a/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index 619bcc8813c..502f8457d8c 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -7,7 +7,7 @@ namespace ts { getAllCodeActions?(context: CodeFixAllContext): CombinedCodeActions; } - export interface CodeFixContextBase extends RefactorOrCodeFixContext { + export interface CodeFixContextBase extends textChanges.TextChangesContext { sourceFile: SourceFile; program: Program; cancellationToken: CancellationToken; @@ -83,7 +83,7 @@ namespace ts { export function codeFixAll(context: CodeFixAllContext, errorCodes: number[], use: (changes: textChanges.ChangeTracker, error: Diagnostic, commands: Push) => void): CombinedCodeActions { const commands: CodeActionCommand[] = []; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => + const changes = textChanges.ChangeTracker.with(context, t => eachDiagnostic(context, errorCodes, diag => use(t, diag, commands))); return createCombinedCodeActions(changes, commands.length === 0 ? undefined : commands); } diff --git a/src/services/codefixes/addMissingInvocationForDecorator.ts b/src/services/codefixes/addMissingInvocationForDecorator.ts index 25214f08218..d063df87be4 100644 --- a/src/services/codefixes/addMissingInvocationForDecorator.ts +++ b/src/services/codefixes/addMissingInvocationForDecorator.ts @@ -5,7 +5,7 @@ namespace ts.codefix { registerCodeFix({ errorCodes, getCodeActions: (context) => { - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => makeChange(t, context.sourceFile, context.span.start)); + const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span.start)); return [{ description: getLocaleSpecificMessage(Diagnostics.Call_decorator_expression), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts index 076ea6135ae..de4498035be 100644 --- a/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts +++ b/src/services/codefixes/correctQualifiedNameToIndexedAccessType.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const qualifiedName = getQualifiedName(context.sourceFile, context.span.start); if (!qualifiedName) return undefined; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, context.sourceFile, qualifiedName)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, context.sourceFile, qualifiedName)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${qualifiedName.left.text}["${qualifiedName.right.text}"]`]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index cf006a6066c..231b3c1513f 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -15,7 +15,7 @@ namespace ts.codefix { return undefined; } - const newLineCharacter = getNewLineFromContext(context); + const newLineCharacter = textChanges.getNewLineFromContext(context); return [{ description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message), @@ -38,7 +38,7 @@ namespace ts.codefix { fixIds: [fixId], // No point applying as a group, doing it once will fix all errors getAllCodeActions: context => codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { if (err.start !== undefined) { - changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineFromContext(context))); + changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, textChanges.getNewLineFromContext(context))); } }), }); diff --git a/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 8e45efa09ad..f7f5aa0a22f 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -96,7 +96,7 @@ namespace ts.codefix { } function getActionsForAddMissingMemberInJavaScriptFile(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, tokenName: string, makeStatic: boolean): CodeFixAction | undefined { - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingMemberInJs(t, classDeclarationSourceFile, classDeclaration, tokenName, makeStatic)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Initialize_static_property_0 : Diagnostics.Initialize_property_0_in_the_constructor), [tokenName]); return { description, changes, fixId }; @@ -144,7 +144,7 @@ namespace ts.codefix { function createAddPropertyDeclarationAction(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, makeStatic: boolean, tokenName: string, typeNode: TypeNode): CodeFixAction { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_property_0 : Diagnostics.Declare_property_0), [tokenName]); - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); + const changes = textChanges.ChangeTracker.with(context, t => addPropertyDeclaration(t, classDeclarationSourceFile, classDeclaration, tokenName, typeNode, makeStatic)); return { description, changes, fixId }; } @@ -176,14 +176,14 @@ namespace ts.codefix { [indexingParameter], typeNode); - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); + const changes = textChanges.ChangeTracker.with(context, t => t.insertNodeAtClassStart(classDeclarationSourceFile, classDeclaration, indexSignature)); // No fixId here because code-fix-all currently only works on adding individual named properties. return { description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Add_index_signature_for_property_0), [tokenName]), changes, fixId: undefined }; } function getActionForMethodDeclaration(context: CodeFixContext, classDeclarationSourceFile: SourceFile, classDeclaration: ClassLikeDeclaration, token: Identifier, callExpression: CallExpression, makeStatic: boolean, inJs: boolean): CodeFixAction | undefined { const description = formatStringFromArgs(getLocaleSpecificMessage(makeStatic ? Diagnostics.Declare_static_method_0 : Diagnostics.Declare_method_0), [token.text]); - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); + const changes = textChanges.ChangeTracker.with(context, t => addMethodDeclaration(t, classDeclarationSourceFile, classDeclaration, token, callExpression, makeStatic, inJs)); return { description, changes, fixId }; } diff --git a/src/services/codefixes/fixAwaitInSyncFunction.ts b/src/services/codefixes/fixAwaitInSyncFunction.ts index 7d09e5f214f..883993e7b51 100644 --- a/src/services/codefixes/fixAwaitInSyncFunction.ts +++ b/src/services/codefixes/fixAwaitInSyncFunction.ts @@ -11,7 +11,7 @@ namespace ts.codefix { const { sourceFile, span } = context; const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, nodes)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, nodes)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_async_modifier_to_containing_function), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts index 6e215666ddf..da3685339ab 100644 --- a/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts +++ b/src/services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts @@ -9,7 +9,7 @@ namespace ts.codefix { errorCodes, getCodeActions(context) { const { program, sourceFile, span } = context; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => + const changes = textChanges.ChangeTracker.with(context, t => addMissingMembers(getClass(sourceFile, span.start), sourceFile, program.getTypeChecker(), t)); return changes.length === 0 ? undefined : [{ description: getLocaleSpecificMessage(Diagnostics.Implement_inherited_abstract_class), changes, fixId }]; }, diff --git a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts index 1eb5bfac986..0236b0c79b7 100644 --- a/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts +++ b/src/services/codefixes/fixClassIncorrectlyImplementsInterface.ts @@ -10,7 +10,7 @@ namespace ts.codefix { const classDeclaration = getClass(sourceFile, span.start); const checker = program.getTypeChecker(); return mapDefined(getClassImplementsHeritageClauseElements(classDeclaration), implementedTypeNode => { - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); + const changes = textChanges.ChangeTracker.with(context, t => addMissingDeclarations(checker, implementedTypeNode, sourceFile, classDeclaration, t)); if (changes.length === 0) return undefined; const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Implement_interface_0), [implementedTypeNode.getText()]); return { description, changes, fixId }; diff --git a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts index 76c1c588097..1595bbf3c13 100644 --- a/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts +++ b/src/services/codefixes/fixClassSuperMustPrecedeThisAccess.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, span.start); if (!nodes) return undefined; const { constructor, superCall } = nodes; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, constructor, superCall)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, constructor, superCall)); return [{ description: getLocaleSpecificMessage(Diagnostics.Make_super_call_the_first_statement_in_the_constructor), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts index 0b747344e3b..8fe2e8458a8 100644 --- a/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts +++ b/src/services/codefixes/fixConstructorForDerivedNeedSuperCall.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile, span } = context; const ctr = getNode(sourceFile, span.start); - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, ctr)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, ctr)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_missing_super_call), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts index a4e91410db5..d98ca556f47 100644 --- a/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts +++ b/src/services/codefixes/fixExtendsInterfaceBecomesImplements.ts @@ -9,7 +9,7 @@ namespace ts.codefix { const nodes = getNodes(sourceFile, context.span.start); if (!nodes) return undefined; const { extendsToken, heritageClauses } = nodes; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChanges(t, sourceFile, extendsToken, heritageClauses)); + const changes = textChanges.ChangeTracker.with(context, t => doChanges(t, sourceFile, extendsToken, heritageClauses)); return [{ description: getLocaleSpecificMessage(Diagnostics.Change_extends_to_implements), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixForgottenThisPropertyAccess.ts b/src/services/codefixes/fixForgottenThisPropertyAccess.ts index 8c337c16c4f..19610da0b15 100644 --- a/src/services/codefixes/fixForgottenThisPropertyAccess.ts +++ b/src/services/codefixes/fixForgottenThisPropertyAccess.ts @@ -7,7 +7,7 @@ namespace ts.codefix { getCodeActions(context) { const { sourceFile } = context; const token = getNode(sourceFile, context.span.start); - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, token)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, token)); return [{ description: getLocaleSpecificMessage(Diagnostics.Add_this_to_unresolved_variable), changes, fixId }]; }, fixIds: [fixId], diff --git a/src/services/codefixes/fixInvalidImportSyntax.ts b/src/services/codefixes/fixInvalidImportSyntax.ts index 0bfc6a6090d..f98f1eaea7c 100644 --- a/src/services/codefixes/fixInvalidImportSyntax.ts +++ b/src/services/codefixes/fixInvalidImportSyntax.ts @@ -32,7 +32,7 @@ namespace ts.codefix { createImportClause(namespace.name, /*namedBindings*/ undefined), node.moduleSpecifier ); - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -48,7 +48,7 @@ namespace ts.codefix { namespace.name, createExternalModuleReference(node.moduleSpecifier) ); - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); changeTracker.replaceNode(sourceFile, node, replacement, { useNonAdjustedEndPosition: true }); const changes = changeTracker.getChanges(); variations.push({ @@ -86,7 +86,7 @@ namespace ts.codefix { addRange(fixes, getCodeFixesForImportDeclaration(context, relatedImport)); } const propertyAccess = createPropertyAccess(expr, "default"); - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); changeTracker.replaceNode(sourceFile, expr, propertyAccess, {}); const changes = changeTracker.getChanges(); fixes.push({ diff --git a/src/services/codefixes/fixSpelling.ts b/src/services/codefixes/fixSpelling.ts index 95159012f48..729f14e9ef9 100644 --- a/src/services/codefixes/fixSpelling.ts +++ b/src/services/codefixes/fixSpelling.ts @@ -12,7 +12,7 @@ namespace ts.codefix { const info = getInfo(sourceFile, context.span.start, context.program.getTypeChecker()); if (!info) return undefined; const { node, suggestion } = info; - const changes = textChanges.ChangeTracker.with(toTextChangesContext(context), t => doChange(t, sourceFile, node, suggestion)); + const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, node, suggestion)); const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_spelling_to_0), [suggestion]); return [{ description, changes, fixId }]; }, diff --git a/src/services/codefixes/fixUnusedIdentifier.ts b/src/services/codefixes/fixUnusedIdentifier.ts index 93c8ae80ac9..b7d948b3deb 100644 --- a/src/services/codefixes/fixUnusedIdentifier.ts +++ b/src/services/codefixes/fixUnusedIdentifier.ts @@ -13,13 +13,13 @@ namespace ts.codefix { const token = getToken(sourceFile, context.span.start); const result: CodeFixAction[] = []; - const deletion = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryDeleteDeclaration(t, sourceFile, token)); + const deletion = textChanges.ChangeTracker.with(context, t => tryDeleteDeclaration(t, sourceFile, token)); if (deletion.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Remove_declaration_for_Colon_0), [token.getText()]); result.push({ description, changes: deletion, fixId: fixIdDelete }); } - const prefix = textChanges.ChangeTracker.with(toTextChangesContext(context), t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); + const prefix = textChanges.ChangeTracker.with(context, t => tryPrefixDeclaration(t, context.errorCode, sourceFile, token)); if (prefix.length) { const description = formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Prefix_0_with_an_underscore), [token.getText()]); result.push({ description, changes: prefix, fixId: fixIdPrefix }); diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 204030e3104..f91d260c81a 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -257,7 +257,7 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { + function getCodeActionForNewImport(context: SymbolContext & textChanges.TextChangesContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { const { kind, sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); @@ -275,7 +275,7 @@ namespace ts.codefix { createIdentifier(symbolName), createExternalModuleReference(quotedModuleSpecifier)); - const changes = ChangeTracker.with(toTextChangesContext(context), changeTracker => { + const changes = ChangeTracker.with(context, changeTracker => { if (lastImportDeclaration) { changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl); } @@ -669,33 +669,33 @@ namespace ts.codefix { return expression && isStringLiteral(expression) ? expression.text : undefined; } - function tryUpdateExistingImport(context: SymbolContext & RefactorOrCodeFixContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { + function tryUpdateExistingImport(context: SymbolContext & textChanges.TextChangesContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { const { symbolName, sourceFile, kind } = context; const { name } = importClause; const { namedBindings } = importClause.kind !== SyntaxKind.ImportEqualsDeclaration && importClause; switch (kind) { case ImportKind.Default: - return name ? undefined : ChangeTracker.with(toTextChangesContext(context), t => + return name ? undefined : ChangeTracker.with(context, t => t.replaceNode(sourceFile, importClause, createImportClause(createIdentifier(symbolName), namedBindings))); case ImportKind.Named: { const newImportSpecifier = createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName)); if (namedBindings && namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length !== 0) { // There are already named imports; add another. - return ChangeTracker.with(toTextChangesContext(context), t => t.insertNodeInListAfter( + return ChangeTracker.with(context, t => t.insertNodeInListAfter( sourceFile, namedBindings.elements[namedBindings.elements.length - 1], newImportSpecifier)); } if (!namedBindings || namedBindings.kind === SyntaxKind.NamedImports && namedBindings.elements.length === 0) { - return ChangeTracker.with(toTextChangesContext(context), t => + return ChangeTracker.with(context, t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamedImports([newImportSpecifier])))); } return undefined; } case ImportKind.Namespace: - return namedBindings ? undefined : ChangeTracker.with(toTextChangesContext(context), t => + return namedBindings ? undefined : ChangeTracker.with(context, t => t.replaceNode(sourceFile, importClause, createImportClause(name, createNamespaceImport(createIdentifier(symbolName))))); case ImportKind.Equals: @@ -706,7 +706,7 @@ namespace ts.codefix { } } - function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext & RefactorOrCodeFixContext, symbolToken: Identifier): ImportCodeAction { + function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext & textChanges.TextChangesContext, symbolToken: Identifier): ImportCodeAction { const { symbolName, sourceFile } = context; /** @@ -720,7 +720,7 @@ namespace ts.codefix { * become "ns.foo" */ // Prefix the node instead of it replacing it, because this may be used for import completions and we don't want the text changes to overlap with the identifier being completed. - const changes = ChangeTracker.with(toTextChangesContext(context), tracker => + const changes = ChangeTracker.with(context, tracker => tracker.changeIdentifierToPropertyAccess(sourceFile, namespacePrefix, symbolToken)); return createCodeAction(Diagnostics.Change_0_to_1, [symbolName, `${namespacePrefix}.${symbolName}`], changes, "CodeChange", /*moduleSpecifier*/ undefined); } diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index b0f5dbab324..6177535ee7d 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -14,24 +14,7 @@ namespace ts { getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined; } - export interface RefactorOrCodeFixContext { - host: LanguageServiceHost; - formatContext: ts.formatting.FormatContext; - } - - export function getNewLineFromContext(context: RefactorOrCodeFixContext) { - const formatSettings = context.formatContext.options; - return (formatSettings && formatSettings.newLineCharacter) || getNewLineOrDefaultFromHost(context.host); - } - - export function toTextChangesContext(context: RefactorOrCodeFixContext): textChanges.TextChangesContext { - return { - newLineCharacter: getNewLineFromContext(context), - formatContext: context.formatContext, - }; - } - - export interface RefactorContext extends RefactorOrCodeFixContext { + export interface RefactorContext extends textChanges.TextChangesContext { file: SourceFile; startPosition: number; endPosition?: number; diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index a39cdc20c42..d3bf59638b2 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -78,7 +78,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { return Debug.fail(`!decl || !jsdocType || decl.type: !${decl} || !${jsdocType} || ${decl.type}`); } - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); const declarationWithType = addType(decl, transformJSDocType(jsdocType) as TypeNode); suppressLeadingAndTrailingTrivia(declarationWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, declarationWithType); @@ -93,7 +93,7 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { const sourceFile = context.file; const token = getTokenAtPosition(sourceFile, context.startPosition, /*includeJsDocComment*/ false); const decl = findAncestor(token, isFunctionLikeDeclaration); - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); const functionWithType = addTypesToFunctionLike(decl); suppressLeadingAndTrailingTrivia(functionWithType); changeTracker.replaceRange(sourceFile, { pos: decl.getStart(), end: decl.end }, functionWithType); diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index 93e39bc683c..cddf40ae017 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -59,7 +59,7 @@ namespace ts.refactor.convertFunctionToES6Class { } const ctorDeclaration = ctorSymbol.valueDeclaration; - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); let precedingNode: Node; let newClassDeclaration: ClassDeclaration; diff --git a/src/services/refactors/convertToEs6Module.ts b/src/services/refactors/convertToEs6Module.ts index 933797554dd..1046bf90aa6 100644 --- a/src/services/refactors/convertToEs6Module.ts +++ b/src/services/refactors/convertToEs6Module.ts @@ -74,7 +74,7 @@ namespace ts.refactor { Debug.assertEqual(actionName, _actionName); const { file, program } = context; Debug.assert(isSourceFileJavaScript(file)); - const edits = textChanges.ChangeTracker.with(toTextChangesContext(context), changes => { + const edits = textChanges.ChangeTracker.with(context, changes => { const moduleExportsChangedToDefault = convertFileToEs6Module(file, program.getTypeChecker(), changes, program.getCompilerOptions().target); if (moduleExportsChangedToDefault) { for (const importingFile of program.getSourceFiles()) { diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 9a9c30756a4..b3110a0ca36 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -806,7 +806,7 @@ namespace ts.refactor.extractSymbol { ); } - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); const minInsertionPos = (isReadonlyArray(range.range) ? last(range.range) : range.range).end; const nodeToInsertBefore = getNodeToInsertFunctionBefore(minInsertionPos, scope); if (nodeToInsertBefore) { @@ -1011,7 +1011,7 @@ namespace ts.refactor.extractSymbol { const initializer = transformConstantInitializer(node, substitutions); suppressLeadingAndTrailingTrivia(initializer); - const changeTracker = textChanges.ChangeTracker.fromContext(toTextChangesContext(context)); + const changeTracker = textChanges.ChangeTracker.fromContext(context); if (isClassLike(scope)) { Debug.assert(!isJS); // See CannotExtractToJSClass diff --git a/src/services/refactors/useDefaultImport.ts b/src/services/refactors/useDefaultImport.ts index e64eaf0bfcb..a103168f67b 100644 --- a/src/services/refactors/useDefaultImport.ts +++ b/src/services/refactors/useDefaultImport.ts @@ -54,7 +54,7 @@ namespace ts.refactor.installTypesForPackage { const newImportClause = createImportClause(name, /*namedBindings*/ undefined); const newImportStatement = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, newImportClause, moduleSpecifier); return { - edits: textChanges.ChangeTracker.with(toTextChangesContext(context), t => t.replaceNode(file, importStatement, newImportStatement)), + edits: textChanges.ChangeTracker.with(context, t => t.replaceNode(file, importStatement, newImportStatement)), renameFilename: undefined, renameLocation: undefined, }; diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 467ba6735ca..c176eef24f2 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -187,10 +187,15 @@ namespace ts.textChanges { } export interface TextChangesContext { - newLineCharacter: string; + host: LanguageServiceHost; formatContext: ts.formatting.FormatContext; } + export function getNewLineFromContext(context: TextChangesContext) { + const formatSettings = context.formatContext.options; + return (formatSettings && formatSettings.newLineCharacter) || getNewLineOrDefaultFromHost(context.host); + } + export class ChangeTracker { private readonly changes: Change[] = []; private readonly newLineCharacter: string; @@ -199,7 +204,7 @@ namespace ts.textChanges { private readonly nodesInsertedAtClassStarts = createMap<{ sourceFile: SourceFile, cls: ClassLikeDeclaration, members: ClassElement[] }>(); public static fromContext(context: TextChangesContext): ChangeTracker { - return new ChangeTracker(context.newLineCharacter === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext); + return new ChangeTracker(getNewLineFromContext(context) === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext); } public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] { From 50fd4762337d64afde54d79fa9e3ceafeff02615 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 17 Jan 2018 16:16:22 -0800 Subject: [PATCH 043/100] Make SymbolContext a subtype of TextChangesContext --- src/services/codefixes/importFixes.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index f91d260c81a..7e0aefef139 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -24,15 +24,13 @@ namespace ts.codefix { moduleSpecifier?: string; } - interface SymbolContext { + interface SymbolContext extends textChanges.TextChangesContext { sourceFile: SourceFile; symbolName: string; - formatContext: ts.formatting.FormatContext; } interface ImportCodeFixContext extends SymbolContext { symbolToken: Identifier | undefined; - host: LanguageServiceHost; program: Program; checker: TypeChecker; compilerOptions: CompilerOptions; @@ -257,7 +255,7 @@ namespace ts.codefix { } } - function getCodeActionForNewImport(context: SymbolContext & textChanges.TextChangesContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { + function getCodeActionForNewImport(context: SymbolContext & { kind: ImportKind }, moduleSpecifier: string): ImportCodeAction { const { kind, sourceFile, symbolName } = context; const lastImportDeclaration = findLast(sourceFile.statements, isAnyImportSyntax); @@ -669,7 +667,7 @@ namespace ts.codefix { return expression && isStringLiteral(expression) ? expression.text : undefined; } - function tryUpdateExistingImport(context: SymbolContext & textChanges.TextChangesContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { + function tryUpdateExistingImport(context: SymbolContext & { kind: ImportKind }, importClause: ImportClause | ImportEqualsDeclaration): FileTextChanges[] | undefined { const { symbolName, sourceFile, kind } = context; const { name } = importClause; const { namedBindings } = importClause.kind !== SyntaxKind.ImportEqualsDeclaration && importClause; @@ -706,7 +704,7 @@ namespace ts.codefix { } } - function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext & textChanges.TextChangesContext, symbolToken: Identifier): ImportCodeAction { + function getCodeActionForUseExistingNamespaceImport(namespacePrefix: string, context: SymbolContext, symbolToken: Identifier): ImportCodeAction { const { symbolName, sourceFile } = context; /** From 29dee9fb0cd695dd4342888bef9ff95fdb05f622 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 17 Jan 2018 16:21:19 -0800 Subject: [PATCH 044/100] Do not expose createWatchOfConfigFile and createWatchOfFilesAndCompilerOptions --- src/compiler/watch.ts | 18 ++---------------- src/harness/unittests/reuseProgramStructure.ts | 4 ++-- src/harness/unittests/tscWatchMode.ts | 6 +++--- tests/baselines/reference/api/typescript.d.ts | 8 -------- 4 files changed, 7 insertions(+), 29 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 0f82544150c..7965af051cc 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -257,7 +257,7 @@ namespace ts { /** * Creates the watch compiler host from system for config file in watch mode */ - export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic: DiagnosticReporter | undefined, reportWatchStatus: WatchStatusReporter | undefined): WatchCompilerHostOfConfigFile { + export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); const host = createWatchCompilerHost(system, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile; host.onConfigFileDiagnostic = reportDiagnostic; @@ -270,7 +270,7 @@ namespace ts { /** * Creates the watch compiler host from system for compiling root files and options in watch mode */ - export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic: DiagnosticReporter | undefined, reportWatchStatus: WatchStatusReporter | undefined): WatchCompilerHostOfFilesAndCompilerOptions { + export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions { const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; @@ -426,20 +426,6 @@ namespace ts { updateRootFileNames(fileNames: string[]): void; } - /** - * Create the watched program for config file - */ - export function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfConfigFile { - return createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, optionsToExtend, system, reportDiagnostic, reportWatchStatus)); - } - - /** - * Create the watched program for root files and compiler options - */ - export function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system = sys, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfFilesAndCompilerOptions { - return createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system, reportDiagnostic, reportWatchStatus)); - } - /** * Creates the watch from the host for root files and compiler options */ diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index a457ad3626a..10a43acbf99 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -910,12 +910,12 @@ namespace ts { } function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) { - const program = createWatchOfFilesAndCompilerOptions(rootFiles, options, system).getCurrentProgram(); + const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system)).getCurrentProgram(); verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); } function verifyProgramWithConfigFile(system: System, configFileName: string) { - const program = createWatchOfConfigFile(configFileName, {}, system).getCurrentProgram(); + const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram(); const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented); verifyProgramIsUptoDate(program, fileNames, options); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 998123ccd01..51ba213abe8 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -23,14 +23,14 @@ namespace ts.tscWatch { } function createWatchOfConfigFile(configFileName: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) { - const compilerHost = ts.createWatchCompilerHostOfConfigFile(configFileName, {}, host, /*reportDiagnostic*/ undefined, /*reportWatchStatus*/ undefined); + const compilerHost = ts.createWatchCompilerHostOfConfigFile(configFileName, {}, host); compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; const watch = ts.createWatchProgram(compilerHost); return () => watch.getCurrentProgram(); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}) { - const watch = ts.createWatchOfFilesAndCompilerOptions(rootFiles, options, host); + const watch = ts.createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, host)); return () => watch.getCurrentProgram(); } @@ -264,7 +264,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([configFile, libFile, file1, file2, file3]); - const watch = ts.createWatchOfConfigFile(configFile.path, {}, host, notImplemented); + const watch = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, host, notImplemented)); checkProgramActualFiles(watch.getCurrentProgram(), [file1.path, libFile.path, file2.path]); checkProgramRootFiles(watch.getCurrentProgram(), [file1.path, file2.path]); diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index dc06cee198e..c64b02bebcb 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4068,14 +4068,6 @@ declare namespace ts { /** Updates the root files in the program, only if this is not config file compilation */ updateRootFileNames(fileNames: string[]): void; } - /** - * Create the watched program for config file - */ - function createWatchOfConfigFile(configFileName: string, optionsToExtend?: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfConfigFile; - /** - * Create the watched program for root files and compiler options - */ - function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system?: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for root files and compiler options */ From f29c0e34fb4a18d8d1584285b7fa4512a9c023ce Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 17 Jan 2018 16:44:47 -0800 Subject: [PATCH 045/100] Expose createWatchCompilerHost as overload --- src/compiler/watch.ts | 14 ++++++++++++++ tests/baselines/reference/api/typescript.d.ts | 5 +++++ 2 files changed, 19 insertions(+) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 7965af051cc..703b5b42cc6 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -426,6 +426,20 @@ namespace ts { updateRootFileNames(fileNames: string[]): void; } + /** + * Create the watch compiler host for either configFile or fileNames and its options + */ + export function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; + export function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; + export function createWatchCompilerHost(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile { + if (isArray(rootFilesOrConfigFileName)) { + return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options, system, reportDiagnostic, reportWatchStatus); + } + else { + return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, system, reportDiagnostic, reportWatchStatus); + } + } + /** * Creates the watch from the host for root files and compiler options */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index c64b02bebcb..e74dd31dc8d 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -4068,6 +4068,11 @@ declare namespace ts { /** Updates the root files in the program, only if this is not config file compilation */ updateRootFileNames(fileNames: string[]): void; } + /** + * Create the watch compiler host for either configFile or fileNames and its options + */ + function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; + function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; /** * Creates the watch from the host for root files and compiler options */ From f0b40180175070ca1097d63cd09b04c6210d3a69 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 18 Jan 2018 08:44:57 -0800 Subject: [PATCH 046/100] Infer:string literal->keyof creates any props not {} --- src/compiler/checker.ts | 4 +- ...inferObjectTypeFromStringLiteralToKeyof.js | 9 ++-- ...ObjectTypeFromStringLiteralToKeyof.symbols | 48 ++++++++++++------- ...erObjectTypeFromStringLiteralToKeyof.types | 25 ++++++++-- ...inferObjectTypeFromStringLiteralToKeyof.ts | 6 ++- 5 files changed, 63 insertions(+), 29 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c1ad93b04aa..b33eda51c26 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10965,7 +10965,7 @@ namespace ts { return type === typeParameter || type.flags & TypeFlags.UnionOrIntersection && forEach((type).types, t => isTypeParameterAtTopLevel(t, typeParameter)); } - /** Create an object with properties named in the string literal type. Every property has type `{}` */ + /** Create an object with properties named in the string literal type. Every property has type `any` */ function createEmptyObjectTypeFromStringLiteral(type: Type) { const members = createSymbolTable(); forEachType(type, t => { @@ -10974,7 +10974,7 @@ namespace ts { } const name = escapeLeadingUnderscores((t as StringLiteralType).value); const literalProp = createSymbol(SymbolFlags.Property, name); - literalProp.type = emptyObjectType; + literalProp.type = anyType; if (t.symbol) { literalProp.declarations = t.symbol.declarations; literalProp.valueDeclaration = t.symbol.valueDeclaration; diff --git a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.js b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.js index 6a9a081ae60..99e7691daaf 100644 --- a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.js +++ b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.js @@ -1,8 +1,11 @@ //// [inferObjectTypeFromStringLiteralToKeyof.ts] -declare function inference(target: T, name: keyof T): void; +declare function inference1(name: keyof T): T; +declare function inference2(target: T, name: keyof T): T; declare var two: "a" | "d"; -inference({ a: 1, b: 2, c: 3, d(n) { return n } }, two); +const x = inference1(two); +const y = inference2({ a: 1, b: 2, c: 3, d(n) { return n } }, two); //// [inferObjectTypeFromStringLiteralToKeyof.js] -inference({ a: 1, b: 2, c: 3, d: function (n) { return n; } }, two); +var x = inference1(two); +var y = inference2({ a: 1, b: 2, c: 3, d: function (n) { return n; } }, two); diff --git a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.symbols b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.symbols index aa3c9e89f9a..b36b47bab65 100644 --- a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.symbols +++ b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.symbols @@ -1,22 +1,36 @@ === tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts === -declare function inference(target: T, name: keyof T): void; ->inference : Symbol(inference, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 0)) ->T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 27)) ->target : Symbol(target, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 30)) ->T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 27)) ->name : Symbol(name, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 40)) ->T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 27)) +declare function inference1(name: keyof T): T; +>inference1 : Symbol(inference1, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 0)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 28)) +>name : Symbol(name, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 31)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 28)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 28)) + +declare function inference2(target: T, name: keyof T): T; +>inference2 : Symbol(inference2, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 49)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 28)) +>target : Symbol(target, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 31)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 28)) +>name : Symbol(name, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 41)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 28)) +>T : Symbol(T, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 28)) declare var two: "a" | "d"; ->two : Symbol(two, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 11)) +>two : Symbol(two, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 11)) -inference({ a: 1, b: 2, c: 3, d(n) { return n } }, two); ->inference : Symbol(inference, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 0)) ->a : Symbol(a, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 11)) ->b : Symbol(b, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 17)) ->c : Symbol(c, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 23)) ->d : Symbol(d, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 29)) ->n : Symbol(n, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 32)) ->n : Symbol(n, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 32)) ->two : Symbol(two, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 1, 11)) +const x = inference1(two); +>x : Symbol(x, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 3, 5)) +>inference1 : Symbol(inference1, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 0)) +>two : Symbol(two, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 11)) + +const y = inference2({ a: 1, b: 2, c: 3, d(n) { return n } }, two); +>y : Symbol(y, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 5)) +>inference2 : Symbol(inference2, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 0, 49)) +>a : Symbol(a, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 22)) +>b : Symbol(b, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 28)) +>c : Symbol(c, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 34)) +>d : Symbol(d, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 40)) +>n : Symbol(n, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 43)) +>n : Symbol(n, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 4, 43)) +>two : Symbol(two, Decl(inferObjectTypeFromStringLiteralToKeyof.ts, 2, 11)) diff --git a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.types b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.types index 39c4bb84196..3218ab453bf 100644 --- a/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.types +++ b/tests/baselines/reference/inferObjectTypeFromStringLiteralToKeyof.types @@ -1,18 +1,33 @@ === tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts === -declare function inference(target: T, name: keyof T): void; ->inference : (target: T, name: keyof T) => void +declare function inference1(name: keyof T): T; +>inference1 : (name: keyof T) => T +>T : T +>name : keyof T +>T : T +>T : T + +declare function inference2(target: T, name: keyof T): T; +>inference2 : (target: T, name: keyof T) => T >T : T >target : T >T : T >name : keyof T >T : T +>T : T declare var two: "a" | "d"; >two : "a" | "d" -inference({ a: 1, b: 2, c: 3, d(n) { return n } }, two); ->inference({ a: 1, b: 2, c: 3, d(n) { return n } }, two) : void ->inference : (target: T, name: keyof T) => void +const x = inference1(two); +>x : { a: any; d: any; } +>inference1(two) : { a: any; d: any; } +>inference1 : (name: keyof T) => T +>two : "a" | "d" + +const y = inference2({ a: 1, b: 2, c: 3, d(n) { return n } }, two); +>y : { a: number; b: number; c: number; d(n: any): any; } +>inference2({ a: 1, b: 2, c: 3, d(n) { return n } }, two) : { a: number; b: number; c: number; d(n: any): any; } +>inference2 : (target: T, name: keyof T) => T >{ a: 1, b: 2, c: 3, d(n) { return n } } : { a: number; b: number; c: number; d(n: any): any; } >a : number >1 : 1 diff --git a/tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts b/tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts index 86acbf93a70..ab5f2618d30 100644 --- a/tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts +++ b/tests/cases/compiler/inferObjectTypeFromStringLiteralToKeyof.ts @@ -1,3 +1,5 @@ -declare function inference(target: T, name: keyof T): void; +declare function inference1(name: keyof T): T; +declare function inference2(target: T, name: keyof T): T; declare var two: "a" | "d"; -inference({ a: 1, b: 2, c: 3, d(n) { return n } }, two); +const x = inference1(two); +const y = inference2({ a: 1, b: 2, c: 3, d(n) { return n } }, two); From bd43e450753224aac950a8e8e51e0d24e78f93b7 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 18 Jan 2018 09:12:01 -0800 Subject: [PATCH 047/100] Move getCurrentDirectory to builder program --- src/compiler/builder.ts | 12 ++++++------ tests/baselines/reference/api/typescript.d.ts | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 13c5a4a2ca4..9c33a1e3f08 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -257,14 +257,14 @@ namespace ts { getSyntacticDiagnostics: (sourceFile, cancellationToken) => state.program.getSyntacticDiagnostics(sourceFile, cancellationToken), getSemanticDiagnostics, emit, - getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile) + getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile), + getCurrentDirectory: () => state.program.getCurrentDirectory() }; if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) { (result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile; } else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { - (result as EmitAndSemanticDiagnosticsBuilderProgram).getCurrentDirectory = () => state.program.getCurrentDirectory(); (result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile; } else { @@ -485,6 +485,10 @@ namespace ts { * in that order would be used to write the files */ emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; + /** + * Get the current directory of the program + */ + getCurrentDirectory(): string; } /** @@ -503,10 +507,6 @@ namespace ts { * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ export interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { - /** - * Get the current directory of the program - */ - getCurrentDirectory(): string; /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index e74dd31dc8d..66a2a8c547a 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -3929,6 +3929,10 @@ declare namespace ts { * in that order would be used to write the files */ emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; + /** + * Get the current directory of the program + */ + getCurrentDirectory(): string; } /** * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files @@ -3945,10 +3949,6 @@ declare namespace ts { * The semantic diagnostics are cached per file and managed by clearing for the changed/affected files */ interface EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { - /** - * Get the current directory of the program - */ - getCurrentDirectory(): string; /** * Emits the next affected file's emit result (EmitResult and sourceFiles emitted) or returns undefined if iteration is complete * The first of writeFile if provided, writeFile of BuilderProgramHost if provided, writeFile of compiler host From 96ac5aa2411f5f4d9465c85691dcf54b3c9985ea Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 18 Jan 2018 12:26:45 -0800 Subject: [PATCH 048/100] Check if the file added is emitted file after validating extensions May fix #21274 --- src/compiler/resolutionCache.ts | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index aac25c5d596..2ed14e0d41e 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -473,11 +473,6 @@ namespace ts { resolutionHost.getCachedDirectoryStructureHost().addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } - // Ignore emits from the program - if (isEmittedFileOfProgram(resolutionHost.getCurrentProgram(), fileOrDirectory)) { - return; - } - // If the files are added to project root or node_modules directory, always run through the invalidation process // Otherwise run through invalidation only if adding to the immediate directory if (!allFilesHaveInvalidatedResolution && @@ -581,6 +576,10 @@ namespace ts { if (!isPathWithDefaultFailedLookupExtension(fileOrDirectoryPath) && !customFailedLookupPaths.has(fileOrDirectoryPath)) { return false; } + // Ignore emits from the program + if (isEmittedFileOfProgram(resolutionHost.getCurrentProgram(), fileOrDirectoryPath)) { + return false; + } // Resolution need to be invalidated if failed lookup location is same as the file or directory getting created isChangedFailedLookupLocation = location => resolutionHost.toPath(location) === fileOrDirectoryPath; } From 225e2f4d78cb360eb376228df0671c7e82e7fbdb Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 18 Jan 2018 12:53:11 -0800 Subject: [PATCH 049/100] Report more detailed info during script debug failure --- src/server/editorServices.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4b6a95e3a82..ade58a5c437 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1810,9 +1810,9 @@ namespace ts.server { let info = this.getScriptInfoForPath(path); if (!info) { const isDynamic = isDynamicFileName(fileName); - Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "Script info with non-dynamic relative file name can only be open script info"); - Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "Open script files with non rooted disk path opened with current directory context cannot have same canonical names"); - Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "Dynamic files must always have current directory context since containing external project name will always match the script info name."); + Debug.assert(isRootedDiskPath(fileName) || isDynamic || openedByClient, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nScript info with non-dynamic relative file name can only be open script info`); + Debug.assert(!isRootedDiskPath(fileName) || this.currentDirectory === currentDirectory || !this.openFilesWithNonRootedDiskPath.has(this.toCanonicalFileName(fileName)), "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nOpen script files with non rooted disk path opened with current directory context cannot have same canonical names`); + Debug.assert(!isDynamic || this.currentDirectory === currentDirectory, "", () => `${JSON.stringify({ fileName, currentDirectory, hostCurrentDirectory: this.currentDirectory, openKeys: arrayFrom(this.openFilesWithNonRootedDiskPath.keys()) })}\nDynamic files must always have current directory context since containing external project name will always match the script info name.`); // If the file is not opened by client and the file doesnot exist on the disk, return if (!openedByClient && !isDynamic && !(hostToQueryFileExistsOn || this.host).fileExists(fileName)) { return; From d97dec85749ce886fcafb9d93082525206b40ec1 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Thu, 18 Jan 2018 13:56:12 -0800 Subject: [PATCH 050/100] Fold newline logic into getNewLineOrDefaultFromHost --- src/services/codefixes/disableJsDiagnostics.ts | 4 ++-- src/services/textChanges.ts | 7 +------ src/services/utilities.ts | 6 ++++-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 231b3c1513f..a887878bac6 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -15,7 +15,7 @@ namespace ts.codefix { return undefined; } - const newLineCharacter = textChanges.getNewLineFromContext(context); + const newLineCharacter = getNewLineOrDefaultFromHost(context.host, context.formatContext.options); return [{ description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message), @@ -38,7 +38,7 @@ namespace ts.codefix { fixIds: [fixId], // No point applying as a group, doing it once will fix all errors getAllCodeActions: context => codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { if (err.start !== undefined) { - changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, textChanges.getNewLineFromContext(context))); + changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineOrDefaultFromHost(context.host, context.formatContext.options))); } }), }); diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index c176eef24f2..3674cd31b97 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -191,11 +191,6 @@ namespace ts.textChanges { formatContext: ts.formatting.FormatContext; } - export function getNewLineFromContext(context: TextChangesContext) { - const formatSettings = context.formatContext.options; - return (formatSettings && formatSettings.newLineCharacter) || getNewLineOrDefaultFromHost(context.host); - } - export class ChangeTracker { private readonly changes: Change[] = []; private readonly newLineCharacter: string; @@ -204,7 +199,7 @@ namespace ts.textChanges { private readonly nodesInsertedAtClassStarts = createMap<{ sourceFile: SourceFile, cls: ClassLikeDeclaration, members: ClassElement[] }>(); public static fromContext(context: TextChangesContext): ChangeTracker { - return new ChangeTracker(getNewLineFromContext(context) === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext); + return new ChangeTracker(getNewLineOrDefaultFromHost(context.host, context.formatContext.options) === "\n" ? NewLineKind.LineFeed : NewLineKind.CarriageReturnLineFeed, context.formatContext); } public static with(context: TextChangesContext, cb: (tracker: ChangeTracker) => void): FileTextChanges[] { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 344a44a9f18..9bb35359523 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1259,8 +1259,10 @@ namespace ts { /** * The default is CRLF. */ - export function getNewLineOrDefaultFromHost(host: LanguageServiceHost | LanguageServiceShimHost) { - return host.getNewLine ? host.getNewLine() : carriageReturnLineFeed; + export function getNewLineOrDefaultFromHost(host: LanguageServiceHost | LanguageServiceShimHost, formatSettings?: FormatCodeSettings) { + return (formatSettings && formatSettings.newLineCharacter) || + (host.getNewLine && host.getNewLine()) || + carriageReturnLineFeed; } export function lineBreakPart() { From ffdba2d01f7cbd0529f8011a1dae5e5721332aa0 Mon Sep 17 00:00:00 2001 From: csigs Date: Thu, 18 Jan 2018 23:11:43 +0000 Subject: [PATCH 051/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ 5 files changed, 30 insertions(+) diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index 66702cf999d..b6b35ec094b 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -897,6 +897,9 @@ + + + @@ -4629,6 +4632,9 @@ + + + diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index 00fd63f90f9..afee7120010 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -885,6 +885,9 @@ + + + @@ -4617,6 +4620,9 @@ + + + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 93c54090734..5d1bc276ee8 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -897,6 +897,9 @@ + + + @@ -4629,6 +4632,9 @@ + + + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index dcd9d3695fc..2481a1b8752 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -888,6 +888,9 @@ + + + @@ -4620,6 +4623,9 @@ + + + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index c8c49d28117..3b9a3824b9f 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -881,6 +881,9 @@ + + + @@ -4613,6 +4616,9 @@ + + + From a6c42a63a1f17adf6a0b196d1c80580bd122ba1a Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 18 Jan 2018 16:39:33 -0800 Subject: [PATCH 052/100] Remove unused properties from interface Refactor (#21286) --- src/compiler/diagnosticMessages.json | 4 ---- src/services/refactorProvider.ts | 11 +++-------- .../refactors/annotateWithTypeFromJSDoc.ts | 17 ++++++----------- .../refactors/convertFunctionToEs6Class.ts | 18 ++++++------------ src/services/refactors/convertToEs6Module.ts | 17 +++++------------ src/services/refactors/extractSymbol.ts | 14 ++++---------- .../refactors/installTypesForPackage.ts | 16 +++++----------- src/services/refactors/useDefaultImport.ts | 17 +++++------------ 8 files changed, 34 insertions(+), 80 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 332aa5cfbd8..7a62d6d5aa4 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -3897,10 +3897,6 @@ "category": "Message", "code": 95002 }, - "Extract symbol": { - "category": "Message", - "code": 95003 - }, "Extract to {0} in {1}": { "category": "Message", "code": 95004 diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 6177535ee7d..3d5957c694c 100644 --- a/src/services/refactorProvider.ts +++ b/src/services/refactorProvider.ts @@ -1,12 +1,6 @@ /* @internal */ namespace ts { export interface Refactor { - /** An unique code associated with each refactor */ - name: string; - - /** Description of the refactor to display in the UI of the editor */ - description: string; - /** Compute the associated code actions */ getEditsForAction(context: RefactorContext, actionName: string): RefactorEditInfo | undefined; @@ -27,8 +21,9 @@ namespace ts { // e.g. nonSuggestableRefactors[refactorCode] -> the refactor you want const refactors: Map = createMap(); - export function registerRefactor(refactor: Refactor) { - refactors.set(refactor.name, refactor); + /** @param name An unique code associated with each refactor. Does not have to be human-readable. */ + export function registerRefactor(name: string, refactor: Refactor) { + refactors.set(name, refactor); } export function getApplicableRefactors(context: RefactorContext): ApplicableRefactorInfo[] { diff --git a/src/services/refactors/annotateWithTypeFromJSDoc.ts b/src/services/refactors/annotateWithTypeFromJSDoc.ts index d3bf59638b2..3116634cb52 100644 --- a/src/services/refactors/annotateWithTypeFromJSDoc.ts +++ b/src/services/refactors/annotateWithTypeFromJSDoc.ts @@ -1,13 +1,10 @@ /* @internal */ namespace ts.refactor.annotateWithTypeFromJSDoc { + const refactorName = "Annotate with type from JSDoc"; const actionName = "annotate"; + const description = Diagnostics.Annotate_with_type_from_JSDoc.message; + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); - const annotateTypeFromJSDoc: Refactor = { - name: "Annotate with type from JSDoc", - description: Diagnostics.Annotate_with_type_from_JSDoc.message, - getEditsForAction, - getAvailableActions - }; type DeclarationWithType = | FunctionLikeDeclaration | VariableDeclaration @@ -15,8 +12,6 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { | PropertySignature | PropertyDeclaration; - registerRefactor(annotateTypeFromJSDoc); - function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { if (isInJavaScriptFile(context.file)) { return undefined; @@ -25,11 +20,11 @@ namespace ts.refactor.annotateWithTypeFromJSDoc { const node = getTokenAtPosition(context.file, context.startPosition, /*includeJsDocComment*/ false); if (hasUsableJSDoc(findAncestor(node, isDeclarationWithType))) { return [{ - name: annotateTypeFromJSDoc.name, - description: annotateTypeFromJSDoc.description, + name: refactorName, + description, actions: [ { - description: annotateTypeFromJSDoc.description, + description, name: actionName } ] diff --git a/src/services/refactors/convertFunctionToEs6Class.ts b/src/services/refactors/convertFunctionToEs6Class.ts index cddf40ae017..6645f8434b6 100644 --- a/src/services/refactors/convertFunctionToEs6Class.ts +++ b/src/services/refactors/convertFunctionToEs6Class.ts @@ -1,16 +1,10 @@ /* @internal */ namespace ts.refactor.convertFunctionToES6Class { + const refactorName = "Convert to ES2015 class"; const actionName = "convert"; - - const convertFunctionToES6Class: Refactor = { - name: "Convert to ES2015 class", - description: Diagnostics.Convert_function_to_an_ES2015_class.message, - getEditsForAction, - getAvailableActions - }; - - registerRefactor(convertFunctionToES6Class); + const description = Diagnostics.Convert_function_to_an_ES2015_class.message; + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { if (!isInJavaScriptFile(context.file)) { @@ -29,11 +23,11 @@ namespace ts.refactor.convertFunctionToES6Class { if ((symbol.flags & SymbolFlags.Function) && symbol.members && (symbol.members.size > 0)) { return [ { - name: convertFunctionToES6Class.name, - description: convertFunctionToES6Class.description, + name: refactorName, + description, actions: [ { - description: convertFunctionToES6Class.description, + description, name: actionName } ] diff --git a/src/services/refactors/convertToEs6Module.ts b/src/services/refactors/convertToEs6Module.ts index 1046bf90aa6..fd73d1c3f13 100644 --- a/src/services/refactors/convertToEs6Module.ts +++ b/src/services/refactors/convertToEs6Module.ts @@ -1,15 +1,8 @@ /* @internal */ namespace ts.refactor { const actionName = "Convert to ES6 module"; - - const convertToEs6Module: Refactor = { - name: actionName, - description: getLocaleSpecificMessage(Diagnostics.Convert_to_ES6_module), - getEditsForAction, - getAvailableActions, - }; - - registerRefactor(convertToEs6Module); + const description = getLocaleSpecificMessage(Diagnostics.Convert_to_ES6_module); + registerRefactor(actionName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { const { file, startPosition } = context; @@ -20,11 +13,11 @@ namespace ts.refactor { const node = getTokenAtPosition(file, startPosition, /*includeJsDocComment*/ false); return !isAtTriggerLocation(file, node) ? undefined : [ { - name: convertToEs6Module.name, - description: convertToEs6Module.description, + name: actionName, + description, actions: [ { - description: convertToEs6Module.description, + description, name: actionName, }, ], diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index b3110a0ca36..479c387fc65 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -3,14 +3,8 @@ /* @internal */ namespace ts.refactor.extractSymbol { - const extractSymbol: Refactor = { - name: "Extract Symbol", - description: getLocaleSpecificMessage(Diagnostics.Extract_symbol), - getAvailableActions, - getEditsForAction, - }; - - registerRefactor(extractSymbol); + const refactorName = "Extract Symbol"; + registerRefactor(refactorName, { getAvailableActions, getEditsForAction }); /** * Compute the associated code actions @@ -77,7 +71,7 @@ namespace ts.refactor.extractSymbol { if (functionActions.length) { infos.push({ - name: extractSymbol.name, + name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_function), actions: functionActions }); @@ -85,7 +79,7 @@ namespace ts.refactor.extractSymbol { if (constantActions.length) { infos.push({ - name: extractSymbol.name, + name: refactorName, description: getLocaleSpecificMessage(Diagnostics.Extract_constant), actions: constantActions }); diff --git a/src/services/refactors/installTypesForPackage.ts b/src/services/refactors/installTypesForPackage.ts index 236ca32c799..4e1d71daf66 100644 --- a/src/services/refactors/installTypesForPackage.ts +++ b/src/services/refactors/installTypesForPackage.ts @@ -1,15 +1,9 @@ /* @internal */ namespace ts.refactor.installTypesForPackage { + const refactorName = "Install missing types package"; const actionName = "install"; - - const installTypesForPackage: Refactor = { - name: "Install missing types package", - description: "Install missing types package", - getEditsForAction, - getAvailableActions, - }; - - registerRefactor(installTypesForPackage); + const description = "Install missing types package"; + registerRefactor(refactorName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { if (getStrictOptionValue(context.program.getCompilerOptions(), "noImplicitAny")) { @@ -20,8 +14,8 @@ namespace ts.refactor.installTypesForPackage { const action = getAction(context); return action && [ { - name: installTypesForPackage.name, - description: installTypesForPackage.description, + name: refactorName, + description, actions: [ { description: action.description, diff --git a/src/services/refactors/useDefaultImport.ts b/src/services/refactors/useDefaultImport.ts index a103168f67b..6ee43cc7503 100644 --- a/src/services/refactors/useDefaultImport.ts +++ b/src/services/refactors/useDefaultImport.ts @@ -1,15 +1,8 @@ /* @internal */ namespace ts.refactor.installTypesForPackage { const actionName = "Convert to default import"; - - const useDefaultImport: Refactor = { - name: actionName, - description: getLocaleSpecificMessage(Diagnostics.Convert_to_default_import), - getEditsForAction, - getAvailableActions, - }; - - registerRefactor(useDefaultImport); + const description = getLocaleSpecificMessage(Diagnostics.Convert_to_default_import); + registerRefactor(actionName, { getEditsForAction, getAvailableActions }); function getAvailableActions(context: RefactorContext): ApplicableRefactorInfo[] | undefined { const { file, startPosition, program } = context; @@ -31,11 +24,11 @@ namespace ts.refactor.installTypesForPackage { return [ { - name: useDefaultImport.name, - description: useDefaultImport.description, + name: actionName, + description, actions: [ { - description: useDefaultImport.description, + description, name: actionName, }, ], From 5bb8d2a590899fc225df2472f41be1b52c41678e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Jan 2018 17:15:48 -0800 Subject: [PATCH 053/100] Properly handle contravariant inferences in inferReverseMappedType --- src/compiler/checker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 79430adf777..519a9f4cf40 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11029,7 +11029,9 @@ namespace ts { const templateType = getTemplateTypeFromMappedType(target); const inference = createInferenceInfo(typeParameter); inferTypes([inference], sourceType, templateType); - return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : emptyObjectType; + return inference.candidates ? getUnionType(inference.candidates, UnionReduction.Subtype) : + inference.contraCandidates ? getCommonSubtype(inference.contraCandidates) : + emptyObjectType; } function getUnmatchedProperty(source: Type, target: Type, requireOptionalProperties: boolean) { From f6c79a631cf3ed3bfac4a1a61727acdb214ce7ba Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Jan 2018 17:28:37 -0800 Subject: [PATCH 054/100] Add regression test --- tests/cases/compiler/reverseMappedContravariantInference.ts | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/cases/compiler/reverseMappedContravariantInference.ts diff --git a/tests/cases/compiler/reverseMappedContravariantInference.ts b/tests/cases/compiler/reverseMappedContravariantInference.ts new file mode 100644 index 00000000000..7b7492f6bad --- /dev/null +++ b/tests/cases/compiler/reverseMappedContravariantInference.ts @@ -0,0 +1,6 @@ +// @strict: true + +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +conforms({ foo: (v: string) => false })({ foo: "hello" }); From 069eac09ecb3d6cbf6bd70fbf4dd09ee3e1623cd Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Thu, 18 Jan 2018 17:28:49 -0800 Subject: [PATCH 055/100] Accept new baselines --- .../reverseMappedContravariantInference.js | 11 ++++++++ ...everseMappedContravariantInference.symbols | 21 ++++++++++++++ .../reverseMappedContravariantInference.types | 28 +++++++++++++++++++ 3 files changed, 60 insertions(+) create mode 100644 tests/baselines/reference/reverseMappedContravariantInference.js create mode 100644 tests/baselines/reference/reverseMappedContravariantInference.symbols create mode 100644 tests/baselines/reference/reverseMappedContravariantInference.types diff --git a/tests/baselines/reference/reverseMappedContravariantInference.js b/tests/baselines/reference/reverseMappedContravariantInference.js new file mode 100644 index 00000000000..7a3980e35aa --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.js @@ -0,0 +1,11 @@ +//// [reverseMappedContravariantInference.ts] +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +conforms({ foo: (v: string) => false })({ foo: "hello" }); + + +//// [reverseMappedContravariantInference.js] +"use strict"; +// Repro from #21273 +conforms({ foo: function (v) { return false; } })({ foo: "hello" }); diff --git a/tests/baselines/reference/reverseMappedContravariantInference.symbols b/tests/baselines/reference/reverseMappedContravariantInference.symbols new file mode 100644 index 00000000000..6f8744a99fe --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/reverseMappedContravariantInference.ts === +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +>conforms : Symbol(conforms, Decl(reverseMappedContravariantInference.ts, 0, 0)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>source : Symbol(source, Decl(reverseMappedContravariantInference.ts, 2, 29)) +>K : Symbol(K, Decl(reverseMappedContravariantInference.ts, 2, 40)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>val : Symbol(val, Decl(reverseMappedContravariantInference.ts, 2, 56)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) +>K : Symbol(K, Decl(reverseMappedContravariantInference.ts, 2, 40)) +>value : Symbol(value, Decl(reverseMappedContravariantInference.ts, 2, 83)) +>T : Symbol(T, Decl(reverseMappedContravariantInference.ts, 2, 26)) + +conforms({ foo: (v: string) => false })({ foo: "hello" }); +>conforms : Symbol(conforms, Decl(reverseMappedContravariantInference.ts, 0, 0)) +>foo : Symbol(foo, Decl(reverseMappedContravariantInference.ts, 3, 10)) +>v : Symbol(v, Decl(reverseMappedContravariantInference.ts, 3, 17)) +>foo : Symbol(foo, Decl(reverseMappedContravariantInference.ts, 3, 41)) + diff --git a/tests/baselines/reference/reverseMappedContravariantInference.types b/tests/baselines/reference/reverseMappedContravariantInference.types new file mode 100644 index 00000000000..1decc716bbf --- /dev/null +++ b/tests/baselines/reference/reverseMappedContravariantInference.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/reverseMappedContravariantInference.ts === +// Repro from #21273 + +declare function conforms(source: { [K in keyof T]: (val: T[K]) => boolean }): (value: T) => boolean; +>conforms : (source: { [K in keyof T]: (val: T[K]) => boolean; }) => (value: T) => boolean +>T : T +>source : { [K in keyof T]: (val: T[K]) => boolean; } +>K : K +>T : T +>val : T[K] +>T : T +>K : K +>value : T +>T : T + +conforms({ foo: (v: string) => false })({ foo: "hello" }); +>conforms({ foo: (v: string) => false })({ foo: "hello" }) : boolean +>conforms({ foo: (v: string) => false }) : (value: { foo: any; }) => boolean +>conforms : (source: { [K in keyof T]: (val: T[K]) => boolean; }) => (value: T) => boolean +>{ foo: (v: string) => false } : { foo: (v: string) => boolean; } +>foo : (v: string) => boolean +>(v: string) => false : (v: string) => boolean +>v : string +>false : false +>{ foo: "hello" } : { foo: string; } +>foo : string +>"hello" : "hello" + From 57e0b22cf3c5f4149dace60256aad6518eb2a778 Mon Sep 17 00:00:00 2001 From: csigs Date: Fri, 19 Jan 2018 05:10:13 +0000 Subject: [PATCH 056/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ .../diagnosticMessages.generated.json.lcl | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index f6a140a47f7..a58de23e3ff 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -888,6 +888,9 @@ + + + @@ -4620,6 +4623,9 @@ + + + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index d6048a235f5..d111d8f84b5 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -897,6 +897,9 @@ + + + @@ -4629,6 +4632,9 @@ + + + diff --git a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl index a36010b60e3..74cf056e95a 100644 --- a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -878,6 +878,9 @@ + + + @@ -4610,6 +4613,9 @@ + + + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index a8529c74fd0..ced875a6581 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -887,6 +887,9 @@ + + + @@ -4619,6 +4622,9 @@ + + + From 1c9cd969662d783a4bfb3883b6b59479d31b258e Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Jan 2018 07:44:18 -0800 Subject: [PATCH 057/100] Avoid spreading array (#21291) --- src/harness/fourslash.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 61b2c088e14..3da46d8dedb 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1305,8 +1305,13 @@ Actual: ${stringify(fullActual)}`); } public verifyRangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }) { - const ranges = ts.isArray(options) ? options : options && options.ranges || this.getRanges(); - this.verifyRenameLocations(ranges, { ranges, ...options }); + if (ts.isArray(options)) { + this.verifyRenameLocations(options, options); + } + else { + const ranges = options && options.ranges || this.getRanges(); + this.verifyRenameLocations(ranges, { ranges, ...options }); + } } public verifyRenameLocations(startRanges: Range | Range[], options: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges: Range[] }) { From d46653a2ac1f744c0abc437dc2144226346050e6 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Jan 2018 10:10:43 -0800 Subject: [PATCH 058/100] Handle `undefined` input to firstDefined (#21300) --- src/compiler/checker.ts | 2 +- src/compiler/core.ts | 4 ++++ src/services/codefixes/importFixes.ts | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 519a9f4cf40..859f0aa1d82 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6562,7 +6562,7 @@ namespace ts { // b) It references `arguments` somewhere const lastParam = lastOrUndefined(declaration.parameters); const lastParamTags = lastParam && getJSDocParameterTags(lastParam); - const lastParamVariadicType = lastParamTags && firstDefined(lastParamTags, p => + const lastParamVariadicType = firstDefined(lastParamTags, p => p.typeExpression && isJSDocVariadicType(p.typeExpression.type) ? p.typeExpression.type : undefined); if (!lastParamVariadicType && !containsArgumentsReference(declaration)) { return false; diff --git a/src/compiler/core.ts b/src/compiler/core.ts index afdc65de51d..cae9c841272 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -183,6 +183,10 @@ namespace ts { /** Like `forEach`, but suitable for use with numbers and strings (which may be falsy). */ export function firstDefined(array: ReadonlyArray | undefined, callback: (element: T, index: number) => U | undefined): U | undefined { + if (array === undefined) { + return undefined; + } + for (let i = 0; i < array.length; i++) { const result = callback(array[i], i); if (result !== undefined) { diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 7e0aefef139..4c536ce3fd2 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -467,7 +467,7 @@ namespace ts.codefix { addJsExtension: boolean, ): string | undefined { const roots = getEffectiveTypeRoots(options, host); - return roots && firstDefined(roots, unNormalizedTypeRoot => { + return firstDefined(roots, unNormalizedTypeRoot => { const typeRoot = toPath(unNormalizedTypeRoot, /*basePath*/ undefined, getCanonicalFileName); if (startsWith(moduleFileName, typeRoot)) { return removeExtensionAndIndexPostFix(moduleFileName.substring(typeRoot.length + 1), options, addJsExtension); From 0a90c67c0788a4a47826923ed658d343ef15180e Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Jan 2018 12:15:41 -0800 Subject: [PATCH 059/100] Support testing definition range of a reference gruop (#21302) --- src/harness/fourslash.ts | 30 +++++++++++++------ src/services/findAllReferences.ts | 2 +- .../cases/fourslash/findAllRefsDefinition.ts | 15 ++++++++++ tests/cases/fourslash/fourslash.ts | 9 ++++-- 4 files changed, 44 insertions(+), 12 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsDefinition.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 3da46d8dedb..bf5f46de248 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -1102,20 +1102,30 @@ namespace FourSlash { } public verifyReferenceGroups(startRanges: Range | Range[], parts: FourSlashInterface.ReferenceGroup[]): void { - const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ definition, ranges: ranges.map(rangeToReferenceEntry) })); + interface ReferenceGroupJson { + definition: string | { text: string, range: ts.TextSpan }; + references: ts.ReferenceEntry[]; + } + const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ + definition: typeof definition === "string" ? definition : { ...definition, range: textSpanFromRange(definition.range) }, + references: ranges.map(rangeToReferenceEntry), + })); for (const startRange of toArray(startRanges)) { this.goToRangeStart(startRange); - const fullActual = ts.map(this.findReferencesAtCaret(), ({ definition, references }) => ({ - definition: definition.displayParts.map(d => d.text).join(""), - ranges: references - })); + const fullActual = ts.map(this.findReferencesAtCaret(), ({ definition, references }, i) => { + const text = definition.displayParts.map(d => d.text).join(""); + return { + definition: typeof fullExpected[i].definition === "string" ? text : { text, range: definition.textSpan }, + references, + }; + }); this.assertObjectsEqual(fullActual, fullExpected); } function rangeToReferenceEntry(r: Range): ts.ReferenceEntry { const { isWriteAccess, isDefinition, isInString } = (r.marker && r.marker.data) || { isWriteAccess: false, isDefinition: false, isInString: undefined }; - const result: ts.ReferenceEntry = { fileName: r.fileName, textSpan: { start: r.start, length: r.end - r.start }, isWriteAccess: !!isWriteAccess, isDefinition: !!isDefinition }; + const result: ts.ReferenceEntry = { fileName: r.fileName, textSpan: textSpanFromRange(r), isWriteAccess: !!isWriteAccess, isDefinition: !!isDefinition }; if (isInString !== undefined) { result.isInString = isInString; } @@ -1139,7 +1149,7 @@ namespace FourSlash { } } - public verifySingleReferenceGroup(definition: string, ranges?: Range[]) { + public verifySingleReferenceGroup(definition: FourSlashInterface.ReferenceGroupDefinition, ranges?: Range[]) { ranges = ranges || this.getRanges(); this.verifyReferenceGroups(ranges, [{ definition, ranges }]); } @@ -4080,7 +4090,7 @@ namespace FourSlashInterface { this.state.verifyNoReferences(markerNameOrRange); } - public singleReferenceGroup(definition: string, ranges?: FourSlash.Range[]) { + public singleReferenceGroup(definition: ReferenceGroupDefinition, ranges?: FourSlash.Range[]) { this.state.verifySingleReferenceGroup(definition, ranges); } @@ -4592,10 +4602,12 @@ namespace FourSlashInterface { } export interface ReferenceGroup { - definition: string; + definition: ReferenceGroupDefinition; ranges: FourSlash.Range[]; } + export type ReferenceGroupDefinition = string | { text: string, range: FourSlash.Range }; + export interface ApplyRefactorOptions { refactorName: string; actionName: string; diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 8b7473357fe..f7529e69d77 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -44,7 +44,7 @@ namespace ts.FindAllReferences { export function findReferencedSymbols(program: Program, cancellationToken: CancellationToken, sourceFiles: ReadonlyArray, sourceFile: SourceFile, position: number): ReferencedSymbol[] | undefined { const referencedSymbols = findAllReferencedSymbols(program, cancellationToken, sourceFiles, sourceFile, position); const checker = program.getTypeChecker(); - return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined(referencedSymbols, ({ definition, references }) => + return !referencedSymbols || !referencedSymbols.length ? undefined : mapDefined(referencedSymbols, ({ definition, references }) => // Only include referenced symbols that have a valid definition. definition && { definition: definitionToReferencedSymbolDefinitionInfo(definition, checker), references: references.map(toReferenceEntry) }); } diff --git a/tests/cases/fourslash/findAllRefsDefinition.ts b/tests/cases/fourslash/findAllRefsDefinition.ts new file mode 100644 index 00000000000..611bacf52ca --- /dev/null +++ b/tests/cases/fourslash/findAllRefsDefinition.ts @@ -0,0 +1,15 @@ +/// + +////const [|{| "isWriteAccess": true, "isDefinition": true |}x|] = 0; +////[|x|]; + +// TODO: GH#21301 + +const ranges = test.ranges(); +const [r0, r1] = ranges; +verify.referenceGroups(r1, [ + { + definition: { text: "const x: 0", range: r1 }, + ranges, + }, +]) diff --git a/tests/cases/fourslash/fourslash.ts b/tests/cases/fourslash/fourslash.ts index 0bf62eaef92..774c2191fd0 100644 --- a/tests/cases/fourslash/fourslash.ts +++ b/tests/cases/fourslash/fourslash.ts @@ -258,8 +258,8 @@ declare namespace FourSlashInterface { * For each of startRanges, asserts the ranges that are referenced from there. * This uses the 'findReferences' command instead of 'getReferencesAtPosition', so references are grouped by their definition. */ - referenceGroups(startRanges: Range | Range[], parts: Array<{ definition: string, ranges: Range[] }>): void; - singleReferenceGroup(definition: string, ranges?: Range[]): void; + referenceGroups(startRanges: Range | Range[], parts: Array<{ definition: ReferencesDefinition, ranges: Range[] }>): void; + singleReferenceGroup(definition: ReferencesDefinition, ranges?: Range[]): void; rangesAreOccurrences(isWriteAccess?: boolean): void; rangesWithSameTextAreRenameLocations(): void; rangesAreRenameLocations(options?: Range[] | { findInStrings?: boolean, findInComments?: boolean, ranges?: Range[] }); @@ -512,6 +512,11 @@ declare namespace FourSlashInterface { textSpan?: TextSpan; }; } + + interface ReferencesDefinition { + text: string; + range: Range; + } } declare function verifyOperationIsCancelled(f: any): void; declare var test: FourSlashInterface.test_; From dcfd6345597be3934e9266cbb8ff3c7e3171cc60 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Jan 2018 13:03:53 -0800 Subject: [PATCH 060/100] in goToDefinition, use array helpers and clean up code (#21304) * in goToDefinition, use array helpers and clean up code * fix lint --- src/services/goToDefinition.ts | 84 ++++++++-------------------------- 1 file changed, 18 insertions(+), 66 deletions(-) diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 5f17eccf9c5..1f7e2ea0be9 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -127,30 +127,16 @@ namespace ts.GoToDefinition { } const symbol = typeChecker.getSymbolAtLocation(node); - if (!symbol) { - return undefined; - } - - const type = typeChecker.getTypeOfSymbolAtLocation(symbol, node); + const type = symbol && typeChecker.getTypeOfSymbolAtLocation(symbol, node); if (!type) { return undefined; } if (type.flags & TypeFlags.Union && !(type.flags & TypeFlags.Enum)) { - const result: DefinitionInfo[] = []; - forEach((type).types, t => { - if (t.symbol) { - addRange(/*to*/ result, /*from*/ getDefinitionFromSymbol(typeChecker, t.symbol, node)); - } - }); - return result; + return flatMap((type).types, t => t.symbol && getDefinitionFromSymbol(typeChecker, t.symbol, node)); } - if (!type.symbol) { - return undefined; - } - - return getDefinitionFromSymbol(typeChecker, type.symbol, node); + return type.symbol && getDefinitionFromSymbol(typeChecker, type.symbol, node); } export function getDefinitionAndBoundSpan(program: Program, sourceFile: SourceFile, position: number): DefinitionInfoAndBoundSpan { @@ -199,66 +185,32 @@ namespace ts.GoToDefinition { } function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node): DefinitionInfo[] { - const result: DefinitionInfo[] = []; - const declarations = symbol.getDeclarations(); const { symbolName, symbolKind, containerName } = getSymbolInfo(typeChecker, symbol, node); + return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(symbol.declarations, declaration => createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && - !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. - forEach(declarations, declaration => { - result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); - }); - } - - return result; - - function tryAddConstructSignature(symbol: Symbol, location: Node, symbolKind: ScriptElementKind, symbolName: string, containerName: string, result: DefinitionInfo[]) { + function getConstructSignatureDefinition(): DefinitionInfo[] | undefined { // Applicable only if we are in a new expression, or we are on a constructor declaration // and in either case the symbol has a construct signature definition, i.e. class - if (isNewExpressionTarget(location) || location.kind === SyntaxKind.ConstructorKeyword) { - if (symbol.flags & SymbolFlags.Class) { - // Find the first class-like declaration and try to get the construct signature. - for (const declaration of symbol.getDeclarations()) { - if (isClassLike(declaration)) { - return tryAddSignature( - declaration.members, /*selectConstructors*/ true, symbolKind, symbolName, containerName, result); - } - } - - Debug.fail("Expected declaration to have at least one class-like declaration"); - } + if (isNewExpressionTarget(node) || node.kind === SyntaxKind.ConstructorKeyword && symbol.flags & SymbolFlags.Class) { + const cls = find(symbol.declarations, isClassLike) || Debug.fail("Expected declaration to have at least one class-like declaration"); + return getSignatureDefinition(cls.members, /*selectConstructors*/ true); } - return false; } - function tryAddCallSignature(symbol: Symbol, location: Node, symbolKind: ScriptElementKind, symbolName: string, containerName: string, result: DefinitionInfo[]) { - if (isCallExpressionTarget(location) || isNewExpressionTarget(location) || isNameOfFunctionDeclaration(location)) { - return tryAddSignature(symbol.declarations, /*selectConstructors*/ false, symbolKind, symbolName, containerName, result); - } - return false; + function getCallSignatureDefinition(): DefinitionInfo[] | undefined { + return isCallExpressionTarget(node) || isNewExpressionTarget(node) || isNameOfFunctionDeclaration(node) + ? getSignatureDefinition(symbol.declarations, /*selectConstructors*/ false) + : undefined; } - function tryAddSignature(signatureDeclarations: ReadonlyArray | undefined, selectConstructors: boolean, symbolKind: ScriptElementKind, symbolName: string, containerName: string, result: DefinitionInfo[]) { + function getSignatureDefinition(signatureDeclarations: ReadonlyArray | undefined, selectConstructors: boolean): DefinitionInfo[] | undefined { if (!signatureDeclarations) { - return false; + return undefined; } - - const declarations: Declaration[] = []; - let definition: Declaration | undefined; - - for (const d of signatureDeclarations) { - if (selectConstructors ? d.kind === SyntaxKind.Constructor : isSignatureDeclaration(d)) { - declarations.push(d); - if ((d).body) definition = d; - } - } - - if (declarations.length) { - result.push(createDefinitionInfo(definition || lastOrUndefined(declarations), symbolKind, symbolName, containerName)); - return true; - } - return false; + const declarations = signatureDeclarations.filter(selectConstructors ? isConstructorDeclaration : isSignatureDeclaration); + return declarations.length + ? [createDefinitionInfo(find(declarations, d => !!(d).body) || last(declarations), symbolKind, symbolName, containerName)] + : undefined; } } From cadd7679a2e3bf819f58e575a650fcf6118a9c96 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Fri, 19 Jan 2018 13:08:22 -0800 Subject: [PATCH 061/100] DT runner:Fix $ExpectError handling Indices into lines of the file are zero-based, but the errors reporting by Typescript are one-based. Also, the regex ignored $ExpectError in tsx files. --- src/harness/externalCompileRunner.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/harness/externalCompileRunner.ts b/src/harness/externalCompileRunner.ts index c829a6490bb..1f945cfcc9c 100644 --- a/src/harness/externalCompileRunner.ts +++ b/src/harness/externalCompileRunner.ts @@ -131,13 +131,13 @@ function removeExpectedErrors(errors: string, cwd: string): string { function isUnexpectedError(cwd: string) { return (error: string[]) => { ts.Debug.assertGreaterThanOrEqual(error.length, 1); - const match = error[0].match(/(.+\.ts)\((\d+),\d+\): error TS/); + const match = error[0].match(/(.+\.tsx?)\((\d+),\d+\): error TS/); if (!match) { return true; } const [, errorFile, lineNumberString] = match; const lines = fs.readFileSync(path.join(cwd, errorFile), { encoding: "utf8" }).split("\n"); - const lineNumber = parseInt(lineNumberString); + const lineNumber = parseInt(lineNumberString) - 1; ts.Debug.assertGreaterThanOrEqual(lineNumber, 0); ts.Debug.assertLessThan(lineNumber, lines.length); const previousLine = lineNumber - 1 > 0 ? lines[lineNumber - 1] : ""; From 2be231d3394db3e0311bdf6345c64b0e6973055e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Fri, 19 Jan 2018 14:59:35 -0800 Subject: [PATCH 062/100] Add createProgram on WatchCompilerHost --- src/compiler/builder.ts | 63 ++++++- src/compiler/tsc.ts | 20 ++- src/compiler/types.ts | 1 + src/compiler/watch.ts | 168 ++++++++---------- .../unittests/reuseProgramStructure.ts | 4 +- src/harness/unittests/tscWatchMode.ts | 14 +- .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 40 +++-- 8 files changed, 178 insertions(+), 133 deletions(-) diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 9c33a1e3f08..886a2194bd9 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -220,9 +220,30 @@ namespace ts { EmitAndSemanticDiagnosticsBuilderProgram } - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; - export function createBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram: BuilderProgram | undefined, kind: BuilderProgramKind) { + export interface BuilderCreationParameters { + newProgram: Program; + host: BuilderProgramHost; + oldProgram: BuilderProgram | undefined; + } + + export function getBuilderCreationParameters(newProgramOrRootNames: Program | ReadonlyArray, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderCreationParameters { + let host: BuilderProgramHost; + let newProgram: Program; + if (isArray(newProgramOrRootNames)) { + newProgram = createProgram(newProgramOrRootNames, hostOrOptions as CompilerOptions, oldProgramOrHost as CompilerHost, oldProgram && oldProgram.getProgram()); + host = oldProgramOrHost as CompilerHost; + } + else { + newProgram = newProgramOrRootNames as Program; + host = hostOrOptions as BuilderProgramHost; + oldProgram = oldProgramOrHost as BuilderProgram; + } + return { host, newProgram, oldProgram }; + } + + export function createBuilderProgram(kind: BuilderProgramKind.SemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): SemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(kind: BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, builderCreationParameters: BuilderCreationParameters): EmitAndSemanticDiagnosticsBuilderProgram; + export function createBuilderProgram(kind: BuilderProgramKind, { newProgram, host, oldProgram }: BuilderCreationParameters) { // Return same program if underlying program doesnt change let oldState = oldProgram && oldProgram.getState(); if (oldState && newProgram === oldState.program) { @@ -518,15 +539,43 @@ namespace ts { /** * Create the builder to manage semantic diagnostics and cache them */ - export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram { - return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.SemanticDiagnosticsBuilderProgram); + export function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; + export function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; + export function createSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | SemanticDiagnosticsBuilderProgram, oldProgram?: SemanticDiagnosticsBuilderProgram) { + return createBuilderProgram(BuilderProgramKind.SemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram)); } /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ - export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram { - return createBuilderProgram(newProgram, host, oldProgram, BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram); + export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + export function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + export function createEmitAndSemanticDiagnosticsBuilderProgram(newProgramOrRootNames: Program | ReadonlyArray, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | EmitAndSemanticDiagnosticsBuilderProgram, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram) { + return createBuilderProgram(BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram, getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram)); + } + + /** + * Creates a builder thats just abstraction over program and can be used with watch + */ + export function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram): BuilderProgram; + export function createAbstractBuilder(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram): BuilderProgram; + export function createAbstractBuilder(newProgramOrRootNames: Program | ReadonlyArray, hostOrOptions: BuilderProgramHost | CompilerOptions, oldProgramOrHost?: CompilerHost | BuilderProgram, oldProgram?: BuilderProgram): BuilderProgram { + const { newProgram: program } = getBuilderCreationParameters(newProgramOrRootNames, hostOrOptions, oldProgramOrHost, oldProgram); + return { + // Only return program, all other methods are not implemented + getProgram: () => program, + getState: notImplemented, + getCompilerOptions: notImplemented, + getSourceFile: notImplemented, + getSourceFiles: notImplemented, + getOptionsDiagnostics: notImplemented, + getGlobalDiagnostics: notImplemented, + getSyntacticDiagnostics: notImplemented, + getSemanticDiagnostics: notImplemented, + emit: notImplemented, + getAllDependencies: notImplemented, + getCurrentDirectory: notImplemented + }; } } diff --git a/src/compiler/tsc.ts b/src/compiler/tsc.ts index 26023e14a8b..15e4867d7f7 100644 --- a/src/compiler/tsc.ts +++ b/src/compiler/tsc.ts @@ -149,12 +149,16 @@ namespace ts { return sys.exit(exitStatus); } - function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { - const compileUsingBuilder = watchCompilerHost.afterProgramCreate; - watchCompilerHost.beforeProgramCreate = enableStatistics; - watchCompilerHost.afterProgramCreate = program => { - compileUsingBuilder(program); - reportStatistics(program); + function updateWatchCompilationHost(watchCompilerHost: WatchCompilerHost) { + const compileUsingBuilder = watchCompilerHost.createProgram; + watchCompilerHost.createProgram = (rootNames, options, host, oldProgram) => { + enableStatistics(options); + return compileUsingBuilder(rootNames, options, host, oldProgram); + }; + const emitFilesUsingBuilder = watchCompilerHost.afterProgramCreate; + watchCompilerHost.afterProgramCreate = builderProgram => { + emitFilesUsingBuilder(builderProgram); + reportStatistics(builderProgram.getProgram()); }; } @@ -163,7 +167,7 @@ namespace ts { } function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { - const watchCompilerHost = ts.createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, reportDiagnostic, createWatchStatusReporter(configParseResult.options)); + const watchCompilerHost = ts.createWatchCompilerHostOfConfigFile(configParseResult.options.configFilePath, optionsToExtend, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(configParseResult.options)); updateWatchCompilationHost(watchCompilerHost); watchCompilerHost.rootFiles = configParseResult.fileNames; watchCompilerHost.options = configParseResult.options; @@ -173,7 +177,7 @@ namespace ts { } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { - const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, reportDiagnostic, createWatchStatusReporter(options)); + const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(options)); updateWatchCompilationHost(watchCompilerHost); createWatchProgram(watchCompilerHost); } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d9bce9381be..f6ca2f45d34 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4471,6 +4471,7 @@ namespace ts { /* @internal */ onReleaseOldSourceFile?(oldSourceFile: SourceFile, oldOptions: CompilerOptions): void; /* @internal */ hasInvalidatedResolution?: HasInvalidatedResolution; /* @internal */ hasChangedAutomaticTypeDirectiveNames?: boolean; + createHash?(data: string): string; } /* @internal */ diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 703b5b42cc6..beccb65f7b8 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -176,16 +176,14 @@ namespace ts { /** * Creates the watch compiler host that can be extended with config file or root file names and options host */ - function createWatchCompilerHost(system = sys, reportDiagnostic: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost { + function createWatchCompilerHost(system = sys, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHost { + if (!createProgram) { + createProgram = createEmitAndSemanticDiagnosticsBuilderProgram as any; + } + let host: DirectoryStructureHost = system; const useCaseSensitiveFileNames = () => system.useCaseSensitiveFileNames; const writeFileName = (s: string) => system.write(s + system.newLine); - const builderProgramHost: BuilderProgramHost = { - useCaseSensitiveFileNames, - createHash: system.createHash && (s => system.createHash(s)), - writeFile - }; - let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined; return { useCaseSensitiveFileNames, getNewLine: () => system.newLine, @@ -208,42 +206,18 @@ namespace ts { createDirectory: path => system.createDirectory(path), writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system, - afterProgramCreate: emitFilesAndReportErrorUsingBuilder, + createHash: system.createHash && (s => system.createHash(s)), + createProgram, + afterProgramCreate: emitFilesAndReportErrorUsingBuilder }; function getDefaultLibLocation() { return getDirectoryPath(normalizePath(system.getExecutingFilePath())); } - function emitFilesAndReportErrorUsingBuilder(program: Program) { - builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, builderProgramHost, builderProgram); + function emitFilesAndReportErrorUsingBuilder(builderProgram: BuilderProgram) { emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName); } - - function ensureDirectoriesExist(directoryPath: string) { - if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { - const parentDirectory = getDirectoryPath(directoryPath); - ensureDirectoriesExist(parentDirectory); - host.createDirectory(directoryPath); - } - } - - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { - try { - performance.mark("beforeIOWrite"); - ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); - - host.writeFile(fileName, text, writeByteOrderMark); - - performance.mark("afterIOWrite"); - performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); - } - catch (e) { - if (onError) { - onError(e.message); - } - } - } } /** @@ -257,9 +231,9 @@ namespace ts { /** * Creates the watch compiler host from system for config file in watch mode */ - export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile { + export function createWatchCompilerHostOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile { reportDiagnostic = reportDiagnostic || createDiagnosticReporter(system); - const host = createWatchCompilerHost(system, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile; + const host = createWatchCompilerHost(system, createProgram, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile; host.onConfigFileDiagnostic = reportDiagnostic; host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); host.configFileName = configFileName; @@ -270,8 +244,8 @@ namespace ts { /** * Creates the watch compiler host from system for compiling root files and options in watch mode */ - export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions { - const host = createWatchCompilerHost(system, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions; + export function createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions { + const host = createWatchCompilerHost(system, createProgram, reportDiagnostic || createDiagnosticReporter(system), reportWatchStatus) as WatchCompilerHostOfFilesAndCompilerOptions; host.rootFiles = rootFiles; host.options = options; return host; @@ -281,12 +255,14 @@ namespace ts { namespace ts { export type DiagnosticReporter = (diagnostic: Diagnostic) => void; export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string) => void; - - export interface WatchCompilerHost { - /** If provided, callback to invoke before each program creation */ - beforeProgramCreate?(compilerOptions: CompilerOptions): void; + export type CreateProgram = (rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: T) => T; + export interface WatchCompilerHost { + /** + * Used to create the program when need for program creation or recreation detected + */ + createProgram: CreateProgram; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(program: Program): void; + afterProgramCreate?(program: T): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; @@ -300,6 +276,7 @@ namespace ts { getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; + createHash?(data: string): string; /** * Use to check file presence for source files and @@ -343,7 +320,7 @@ namespace ts { /** Internal interface used to wire emit through same host */ /*@internal*/ - export interface WatchCompilerHost { + export interface WatchCompilerHost { createDirectory?(path: string): void; writeFile?(path: string, data: string, writeByteOrderMark?: boolean): void; onCachedDirectoryStructureHostCreate?(host: CachedDirectoryStructureHost): void; @@ -352,7 +329,7 @@ namespace ts { /** * Host to create watch with root files and options */ - export interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { + export interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { /** root files to use to generate program */ rootFiles: string[]; @@ -378,7 +355,7 @@ namespace ts { /** * Host to create watch with config file */ - export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { /** Name of the config file to compile */ configFileName: string; @@ -396,7 +373,7 @@ namespace ts { * Host to create watch with config file that is already parsed (from tsc) */ /*@internal*/ - export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { rootFiles?: string[]; options?: CompilerOptions; optionsToExtend?: CompilerOptions; @@ -429,33 +406,33 @@ namespace ts { /** * Create the watch compiler host for either configFile or fileNames and its options */ - export function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; - export function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; - export function createWatchCompilerHost(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile { + export function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; + export function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; + export function createWatchCompilerHost(rootFilesOrConfigFileName: string | string[], options: CompilerOptions | undefined, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions | WatchCompilerHostOfConfigFile { if (isArray(rootFilesOrConfigFileName)) { - return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options, system, reportDiagnostic, reportWatchStatus); + return createWatchCompilerHostOfFilesAndCompilerOptions(rootFilesOrConfigFileName, options, system, createProgram, reportDiagnostic, reportWatchStatus); } else { - return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, system, reportDiagnostic, reportWatchStatus); + return createWatchCompilerHostOfConfigFile(rootFilesOrConfigFileName, options, system, createProgram, reportDiagnostic, reportWatchStatus); } } /** * Creates the watch from the host for root files and compiler options */ - export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; + export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - export function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; - export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + export function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { interface HostFileInfo { version: number; sourceFile: SourceFile; fileWatcher: FileWatcher; } - let program: Program; + let builderProgram: T; let reloadLevel: ConfigFileProgramReloadLevel; // level to indicate if the program needs to be reloaded from config file/just filenames etc let missingFilesMap: Map; // Map of file watchers for the missing files let watchedWildcardDirectories: Map; // map of watchers for the wild card directories in the config file @@ -470,7 +447,7 @@ namespace ts { const currentDirectory = host.getCurrentDirectory(); const getCurrentDirectory = () => currentDirectory; const readFile: (path: string, encoding?: string) => string | undefined = (path, encoding) => host.readFile(path, encoding); - const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {} } = host; + const { configFileName, optionsToExtend: optionsToExtendForConfigFile = {}, createProgram } = host; let { rootFiles: rootFileNames, options: compilerOptions, configFileSpecs, configFileWildCardDirectories } = host; const cachedDirectoryStructureHost = configFileName && createCachedDirectoryStructureHost(host, currentDirectory, useCaseSensitiveFileNames); @@ -513,7 +490,7 @@ namespace ts { getSourceFileByPath: getVersionedSourceFileByPath, getDefaultLibLocation: host.getDefaultLibLocation && (() => host.getDefaultLibLocation()), getDefaultLibFileName: options => host.getDefaultLibFileName(options), - writeFile: notImplemented, + writeFile, getCurrentDirectory, useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, getCanonicalFileName, @@ -526,6 +503,7 @@ namespace ts { realpath: host.realpath && (s => host.realpath(s)), getEnvironmentVariable: host.getEnvironmentVariable ? (name => host.getEnvironmentVariable(name)) : (() => ""), onReleaseOldSourceFile, + createHash: host.createHash && (data => host.createHash(data)), // Members for ResolutionCacheHost toPath, getCompilationSettings: () => compilerOptions, @@ -563,16 +541,21 @@ namespace ts { watchConfigFileWildCardDirectories(); return configFileName ? - { getCurrentProgram, getProgram: synchronizeProgram } : - { getCurrentProgram, getProgram: synchronizeProgram, updateRootFileNames }; + { getCurrentProgram: getCurrentBuilderProgram, getProgram: synchronizeProgram } : + { getCurrentProgram: getCurrentBuilderProgram, getProgram: synchronizeProgram, updateRootFileNames }; - function getCurrentProgram() { - return program; + function getCurrentBuilderProgram() { + return builderProgram; } - function synchronizeProgram(): Program { + function getCurrentProgram() { + return builderProgram && builderProgram.getProgram(); + } + + function synchronizeProgram() { writeLog(`Synchronizing program`); + const program = getCurrentProgram(); if (hasChangedCompilerOptions) { newLine = updateNewLine(); if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) { @@ -582,12 +565,8 @@ namespace ts { // All resolutions are invalid if user provided resolutions const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution); - if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { - return program; - } - - if (host.beforeProgramCreate) { - host.beforeProgramCreate(compilerOptions); + if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { + return builderProgram; } // Compile the program @@ -596,11 +575,11 @@ namespace ts { resolutionCache.startCachingPerDirectoryResolution(); compilerHost.hasInvalidatedResolution = hasInvalidatedResolution; compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames; - program = createProgram(rootFileNames, compilerOptions, compilerHost, program); + builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram); resolutionCache.finishCachingPerDirectoryResolution(); // Update watches - updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath); + updateMissingFilePathsWatch(builderProgram.getProgram(), missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath); if (needsUpdateInTypeRootWatch) { resolutionCache.updateTypeRootsWatch(); } @@ -620,10 +599,10 @@ namespace ts { } if (host.afterProgramCreate) { - host.afterProgramCreate(program); + host.afterProgramCreate(builderProgram); } reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); - return program; + return builderProgram; } function updateRootFileNames(files: string[]) { @@ -928,23 +907,30 @@ namespace ts { flags ); } - } - /** - * Creates the watch from the host for root files and compiler options - */ - export function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; - /** - * Creates the watch from the host for config file - */ - export function createWatchBuilderProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; - export function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { - const watch = createWatchProgram(host); - let builderProgram: T | undefined; - return { - getProgram: () => builderProgram = createBuilderProgram(watch.getProgram(), host, builderProgram), - getCurrentProgram: () => builderProgram = createBuilderProgram(watch.getCurrentProgram(), host, builderProgram), - updateRootFileNames: watch.updateRootFileNames && (fileNames => watch.updateRootFileNames(fileNames)) - }; + function ensureDirectoriesExist(directoryPath: string) { + if (directoryPath.length > getRootLength(directoryPath) && !host.directoryExists(directoryPath)) { + const parentDirectory = getDirectoryPath(directoryPath); + ensureDirectoriesExist(parentDirectory); + host.createDirectory(directoryPath); + } + } + + function writeFile(fileName: string, text: string, writeByteOrderMark: boolean, onError: (message: string) => void) { + try { + performance.mark("beforeIOWrite"); + ensureDirectoriesExist(getDirectoryPath(normalizePath(fileName))); + + host.writeFile(fileName, text, writeByteOrderMark); + + performance.mark("afterIOWrite"); + performance.measure("I/O Write", "beforeIOWrite", "afterIOWrite"); + } + catch (e) { + if (onError) { + onError(e.message); + } + } + } } } diff --git a/src/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 10a43acbf99..454e97b3133 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -910,12 +910,12 @@ namespace ts { } function verifyProgramWithoutConfigFile(system: System, rootFiles: string[], options: CompilerOptions) { - const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system)).getCurrentProgram(); + const program = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system)).getCurrentProgram().getProgram(); verifyProgramIsUptoDate(program, duplicate(rootFiles), duplicate(options)); } function verifyProgramWithConfigFile(system: System, configFileName: string) { - const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram(); + const program = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram().getProgram(); const { fileNames, options } = parseConfigFileWithSystem(configFileName, {}, system, notImplemented); verifyProgramIsUptoDate(program, fileNames, options); } diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 51ba213abe8..a2b79b00dc7 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -25,13 +25,13 @@ namespace ts.tscWatch { function createWatchOfConfigFile(configFileName: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) { const compilerHost = ts.createWatchCompilerHostOfConfigFile(configFileName, {}, host); compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; - const watch = ts.createWatchProgram(compilerHost); - return () => watch.getCurrentProgram(); + const watch = createWatchProgram(compilerHost); + return () => watch.getCurrentProgram().getProgram(); } function createWatchOfFilesAndCompilerOptions(rootFiles: string[], host: WatchedSystem, options: CompilerOptions = {}) { - const watch = ts.createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, host)); - return () => watch.getCurrentProgram(); + const watch = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, host)); + return () => watch.getCurrentProgram().getProgram(); } function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { @@ -264,10 +264,10 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([configFile, libFile, file1, file2, file3]); - const watch = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, host, notImplemented)); + const watch = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, host, /*createProgram*/ undefined, notImplemented)); - checkProgramActualFiles(watch.getCurrentProgram(), [file1.path, libFile.path, file2.path]); - checkProgramRootFiles(watch.getCurrentProgram(), [file1.path, file2.path]); + checkProgramActualFiles(watch.getCurrentProgram().getProgram(), [file1.path, libFile.path, file2.path]); + checkProgramRootFiles(watch.getCurrentProgram().getProgram(), [file1.path, file2.path]); checkWatchedFiles(host, [configFile.path, file1.path, file2.path, libFile.path]); const configDir = getDirectoryPath(configFile.path); checkWatchedDirectories(host, [configDir, combinePaths(configDir, projectSystem.nodeModulesAtTypes)], /*recursive*/ true); diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 2ea2defee8b..5ab72e34a0a 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2506,6 +2506,7 @@ declare namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string; + createHash?(data: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 66a2a8c547a..3cbca96f8fa 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2506,6 +2506,7 @@ declare namespace ts { */ resolveTypeReferenceDirectives?(typeReferenceDirectiveNames: string[], containingFile: string): (ResolvedTypeReferenceDirective | undefined)[]; getEnvironmentVariable?(name: string): string; + createHash?(data: string): string; } interface SourceMapRange extends TextRange { source?: SourceMapSource; @@ -3960,20 +3961,30 @@ declare namespace ts { * Create the builder to manage semantic diagnostics and cache them */ function createSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; + function createSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: SemanticDiagnosticsBuilderProgram): SemanticDiagnosticsBuilderProgram; /** * Create the builder that can handle the changes in program and iterate through changed files * to emit the those files and manage semantic diagnostics cache as well */ function createEmitAndSemanticDiagnosticsBuilderProgram(newProgram: Program, host: BuilderProgramHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + function createEmitAndSemanticDiagnosticsBuilderProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: EmitAndSemanticDiagnosticsBuilderProgram): EmitAndSemanticDiagnosticsBuilderProgram; + /** + * Creates a builder thats just abstraction over program and can be used with watch + */ + function createAbstractBuilder(newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram): BuilderProgram; + function createAbstractBuilder(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: BuilderProgram): BuilderProgram; } declare namespace ts { type DiagnosticReporter = (diagnostic: Diagnostic) => void; type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string) => void; - interface WatchCompilerHost { - /** If provided, callback to invoke before each program creation */ - beforeProgramCreate?(compilerOptions: CompilerOptions): void; + type CreateProgram = (rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: T) => T; + interface WatchCompilerHost { + /** + * Used to create the program when need for program creation or recreation detected + */ + createProgram: CreateProgram; /** If provided, callback to invoke after every new program creation */ - afterProgramCreate?(program: Program): void; + afterProgramCreate?(program: T): void; /** If provided, called with Diagnostic message that informs about change in watch status */ onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; useCaseSensitiveFileNames(): boolean; @@ -3981,6 +3992,7 @@ declare namespace ts { getCurrentDirectory(): string; getDefaultLibFileName(options: CompilerOptions): string; getDefaultLibLocation?(): string; + createHash?(data: string): string; /** * Use to check file presence for source files and * if resolveModuleNames is not provided (complier is in charge of module resolution) then module files as well @@ -4019,7 +4031,7 @@ declare namespace ts { /** * Host to create watch with root files and options */ - interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { + interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { /** root files to use to generate program */ rootFiles: string[]; /** Compiler options */ @@ -4041,7 +4053,7 @@ declare namespace ts { /** * Host to create watch with config file */ - interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { + interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { /** Name of the config file to compile */ configFileName: string; /** Options to extend */ @@ -4071,24 +4083,16 @@ declare namespace ts { /** * Create the watch compiler host for either configFile or fileNames and its options */ - function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; - function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; + function createWatchCompilerHost(rootFiles: string[], options: CompilerOptions, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfFilesAndCompilerOptions; + function createWatchCompilerHost(configFileName: string, optionsToExtend: CompilerOptions | undefined, system: System, createProgram?: CreateProgram, reportDiagnostic?: DiagnosticReporter, reportWatchStatus?: WatchStatusReporter): WatchCompilerHostOfConfigFile; /** * Creates the watch from the host for root files and compiler options */ - function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; + function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions): WatchOfFilesAndCompilerOptions; /** * Creates the watch from the host for config file */ - function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; - /** - * Creates the watch from the host for root files and compiler options - */ - function createWatchBuilderProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfFilesAndCompilerOptions; - /** - * Creates the watch from the host for config file - */ - function createWatchBuilderProgram(host: WatchCompilerHostOfConfigFile & BuilderProgramHost, createBuilderProgram: (newProgram: Program, host: BuilderProgramHost, oldProgram?: T) => T): WatchOfConfigFile; + function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; } declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; From 9db45dff6dc319ad35c96b79ec9587d9ef5d33ee Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 19 Jan 2018 15:58:35 -0800 Subject: [PATCH 063/100] Create a 'configure-insiders' and 'publish-insiders' task. --- Jakefile.js | 15 ++++++++++++++- scripts/configureNightly.ts | 27 ++++++++++++++++----------- 2 files changed, 30 insertions(+), 12 deletions(-) diff --git a/Jakefile.js b/Jakefile.js index 2026544a249..54b7e3af94d 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -575,7 +575,7 @@ task("setDebugMode", function () { }); task("configure-nightly", [configureNightlyJs], function () { - var cmd = host + " " + configureNightlyJs + " " + packageJson + " " + versionFile; + var cmd = host + " " + configureNightlyJs + " dev " + packageJson + " " + versionFile; console.log(cmd); exec(cmd); }, { async: true }); @@ -587,6 +587,19 @@ task("publish-nightly", ["configure-nightly", "LKG", "clean", "setDebugMode", "r exec(cmd); }); +task("configure-insiders", [configureNightlyJs], function () { + var cmd = host + " " + configureNightlyJs + " insiders " + packageJson + " " + versionFile; + console.log(cmd); + exec(cmd); +}, { async: true }); + +desc("Configure, build, test, and publish the insiders release."); +task("publish-insiders", ["configure-nightly", "LKG", "clean", "setDebugMode", "runtests-parallel"], function () { + var cmd = "npm publish --tag insiders"; + console.log(cmd); + exec(cmd); +}); + var importDefinitelyTypedTestsDirectory = path.join(scriptsDirectory, "importDefinitelyTypedTests"); var importDefinitelyTypedTestsJs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.js"); var importDefinitelyTypedTestsTs = path.join(importDefinitelyTypedTestsDirectory, "importDefinitelyTypedTests.ts"); diff --git a/scripts/configureNightly.ts b/scripts/configureNightly.ts index 778f5ce3727..a63490ca051 100644 --- a/scripts/configureNightly.ts +++ b/scripts/configureNightly.ts @@ -11,34 +11,39 @@ interface PackageJson { function main(): void { const sys = ts.sys; - if (sys.args.length < 2) { + if (sys.args.length < 3) { sys.write("Usage:" + sys.newLine) - sys.write("\tnode configureNightly.js " + sys.newLine); + sys.write("\tnode configureNightly.js " + sys.newLine); return; } + const tag = sys.args[0]; + if (tag !== "dev" && tag !== "insiders") { + throw new Error(`Unexpected tag name '${tag}'.`); + } + // Acquire the version from the package.json file and modify it appropriately. - const packageJsonFilePath = ts.normalizePath(sys.args[0]); + const packageJsonFilePath = ts.normalizePath(sys.args[1]); const packageJsonValue: PackageJson = JSON.parse(sys.readFile(packageJsonFilePath)); const { majorMinor, patch } = parsePackageJsonVersion(packageJsonValue.version); - const nightlyPatch = getNightlyPatch(patch); + const prereleasePatch = getPrereleasePatch(tag, patch); // Acquire and modify the source file that exposes the version string. - const tsFilePath = ts.normalizePath(sys.args[1]); + const tsFilePath = ts.normalizePath(sys.args[2]); const tsFileContents = ts.sys.readFile(tsFilePath); - const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, nightlyPatch); + const modifiedTsFileContents = updateTsFile(tsFilePath, tsFileContents, majorMinor, patch, prereleasePatch); // Ensure we are actually changing something - the user probably wants to know that the update failed. if (tsFileContents === modifiedTsFileContents) { - let err = `\n '${tsFilePath}' was not updated while configuring for a nightly publish.\n `; + let err = `\n '${tsFilePath}' was not updated while configuring for a prerelease publish for '${tag}'.\n `; err += `Ensure that you have not already run this script; otherwise, erase your changes using 'git checkout -- "${tsFilePath}"'.`; - throw err + "\n"; + throw new Error(err + "\n"); } // Finally write the changes to disk. // Modify the package.json structure - packageJsonValue.version = `${majorMinor}.${nightlyPatch}`; + packageJsonValue.version = `${majorMinor}.${prereleasePatch}`; sys.writeFile(packageJsonFilePath, JSON.stringify(packageJsonValue, /*replacer:*/ undefined, /*space:*/ 4)) sys.writeFile(tsFilePath, modifiedTsFileContents); } @@ -69,7 +74,7 @@ function parsePackageJsonVersion(versionString: string): { majorMinor: string, p } /** e.g. 0-dev.20170707 */ -function getNightlyPatch(plainPatch: string): string { +function getPrereleasePatch(tag: string, plainPatch: string): string { // We're going to append a representation of the current time at the end of the current version. // String.prototype.toISOString() returns a 24-character string formatted as 'YYYY-MM-DDTHH:mm:ss.sssZ', // but we'd prefer to just remove separators and limit ourselves to YYYYMMDD. @@ -77,7 +82,7 @@ function getNightlyPatch(plainPatch: string): string { const now = new Date(); const timeStr = now.toISOString().replace(/:|T|\.|-/g, "").slice(0, 8); - return `${plainPatch}-dev.${timeStr}`; + return `${plainPatch}-${tag}.${timeStr}`; } main(); \ No newline at end of file From 6b9ea7cab8158ed423a711ae759271277848dde7 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Fri, 19 Jan 2018 16:03:02 -0800 Subject: [PATCH 064/100] configureNightly -> configurePrerelease --- .gitignore | 2 +- Jakefile.js | 20 +++++++++---------- ...igureNightly.ts => configurePrerelease.ts} | 0 3 files changed, 11 insertions(+), 11 deletions(-) rename scripts/{configureNightly.ts => configurePrerelease.ts} (100%) diff --git a/.gitignore b/.gitignore index 5125de360b8..2e90e788faa 100644 --- a/.gitignore +++ b/.gitignore @@ -39,7 +39,7 @@ scripts/word2md.js scripts/buildProtocol.js scripts/ior.js scripts/authors.js -scripts/configureNightly.js +scripts/configurePrerelease.js scripts/processDiagnosticMessages.d.ts scripts/processDiagnosticMessages.js scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js diff --git a/Jakefile.js b/Jakefile.js index 54b7e3af94d..e8ba3f53de3 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -556,16 +556,16 @@ desc("Generates a diagnostic file in TypeScript based on an input JSON file"); task("generate-diagnostics", [diagnosticInfoMapTs]); // Publish nightly -var configureNightlyJs = path.join(scriptsDirectory, "configureNightly.js"); -var configureNightlyTs = path.join(scriptsDirectory, "configureNightly.ts"); +var configurePrereleaseJs = path.join(scriptsDirectory, "configurePrerelease.js"); +var configurePrereleaseTs = path.join(scriptsDirectory, "configurePrerelease.ts"); var packageJson = "package.json"; var versionFile = path.join(compilerDirectory, "core.ts"); -file(configureNightlyTs); +file(configurePrereleaseTs); -compileFile(/*outfile*/configureNightlyJs, - /*sources*/[configureNightlyTs], - /*prereqs*/[configureNightlyTs], +compileFile(/*outfile*/configurePrereleaseJs, + /*sources*/[configurePrereleaseTs], + /*prereqs*/[configurePrereleaseTs], /*prefixes*/[], /*useBuiltCompiler*/ false, { noOutFile: false, generateDeclarations: false, keepComments: false, noResolve: false, stripInternal: false }); @@ -574,8 +574,8 @@ task("setDebugMode", function () { useDebugMode = true; }); -task("configure-nightly", [configureNightlyJs], function () { - var cmd = host + " " + configureNightlyJs + " dev " + packageJson + " " + versionFile; +task("configure-nightly", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " dev " + packageJson + " " + versionFile; console.log(cmd); exec(cmd); }, { async: true }); @@ -587,8 +587,8 @@ task("publish-nightly", ["configure-nightly", "LKG", "clean", "setDebugMode", "r exec(cmd); }); -task("configure-insiders", [configureNightlyJs], function () { - var cmd = host + " " + configureNightlyJs + " insiders " + packageJson + " " + versionFile; +task("configure-insiders", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " insiders " + packageJson + " " + versionFile; console.log(cmd); exec(cmd); }, { async: true }); diff --git a/scripts/configureNightly.ts b/scripts/configurePrerelease.ts similarity index 100% rename from scripts/configureNightly.ts rename to scripts/configurePrerelease.ts From 6224d51f84cc2daf07e2ba69ed3392dfa8b96fec Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 19 Jan 2018 16:04:30 -0800 Subject: [PATCH 065/100] For `{ type: "a" } | { type: "b" }`, find references for the union property (#21298) --- src/services/findAllReferences.ts | 10 +++++++--- .../cases/fourslash/findAllRefsUnionProperty.ts | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) create mode 100644 tests/cases/fourslash/findAllRefsUnionProperty.ts diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index f7529e69d77..31a0702bb82 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -356,7 +356,7 @@ namespace ts.FindAllReferences.Core { /** Core find-all-references algorithm for a normal symbol. */ function getReferencedSymbolsForSymbol(symbol: Symbol, node: Node, sourceFiles: ReadonlyArray, checker: TypeChecker, cancellationToken: CancellationToken, options: Options): SymbolAndEntries[] { - symbol = skipPastExportOrImportSpecifier(symbol, node, checker); + symbol = skipPastExportOrImportSpecifierOrUnion(symbol, node, checker); // Compute the meaning from the location and the symbol it references const searchMeaning = getIntersectingMeaningFromDeclarations(getMeaningFromLocation(node), symbol.declarations); @@ -405,7 +405,7 @@ namespace ts.FindAllReferences.Core { } /** Handle a few special cases relating to export/import specifiers. */ - function skipPastExportOrImportSpecifier(symbol: Symbol, node: Node, checker: TypeChecker): Symbol { + function skipPastExportOrImportSpecifierOrUnion(symbol: Symbol, node: Node, checker: TypeChecker): Symbol { const { parent } = node; if (isExportSpecifier(parent)) { return getLocalSymbolForExportSpecifier(node as Identifier, symbol, parent, checker); @@ -415,7 +415,11 @@ namespace ts.FindAllReferences.Core { return checker.getImmediateAliasedSymbol(symbol); } - return symbol; + // If the symbol is declared as part of a declaration like `{ type: "a" } | { type: "b" }`, use the property on the union type to get more references. + return firstDefined(symbol.declarations, decl => + isTypeLiteralNode(decl.parent) && isUnionTypeNode(decl.parent.parent) + ? checker.getPropertyOfType(checker.getTypeFromTypeNode(decl.parent.parent), symbol.name) + : undefined) || symbol; } /** diff --git a/tests/cases/fourslash/findAllRefsUnionProperty.ts b/tests/cases/fourslash/findAllRefsUnionProperty.ts new file mode 100644 index 00000000000..ddbfd54144f --- /dev/null +++ b/tests/cases/fourslash/findAllRefsUnionProperty.ts @@ -0,0 +1,16 @@ +/// + +////type T = +//// | { [|{| "isWriteAccess": true, "isDefinition": true |}type|]: "a" } +//// | { [|{| "isWriteAccess": true, "isDefinition": true |}type|]: "b" }; +////declare const t: T; +////if (t.[|type|] !== "failure") { +//// t.[|type|]; +////} + +const ranges = test.ranges(); +const [r0, r1, r2, r3] = ranges; +verify.referenceGroups(ranges, [ + { definition: '(property) type: "a"', ranges: [r0, r2, r3] }, // TODO: this have type `"a" | "b"` + { definition: '(property) type: "b"', ranges: [r1] }, +]); From d4c36120cfa0533ace43497d7843a02ff8960578 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Fri, 19 Jan 2018 16:06:42 -0800 Subject: [PATCH 066/100] Make nonnull assertions and binding patterns apparent declared type locations (#20995) * Use apparent type of original type to handle indexes * Redo older fix causing new bug by extending getDeclaredOrApparentType instead of getTypeWithFacts * Rename symbol --- src/compiler/checker.ts | 24 ++++------ ...efiniteAssignmentOfDestructuredVariable.js | 28 ++++++++++++ ...teAssignmentOfDestructuredVariable.symbols | 42 +++++++++++++++++ ...niteAssignmentOfDestructuredVariable.types | 45 +++++++++++++++++++ ...strictNullNotNullIndexTypeShouldWork.types | 8 ++-- ...efiniteAssignmentOfDestructuredVariable.ts | 16 +++++++ 6 files changed, 143 insertions(+), 20 deletions(-) create mode 100644 tests/baselines/reference/definiteAssignmentOfDestructuredVariable.js create mode 100644 tests/baselines/reference/definiteAssignmentOfDestructuredVariable.symbols create mode 100644 tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types create mode 100644 tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1e66ee7cbb4..ee2458b0a3e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -3949,7 +3949,8 @@ namespace ts { if (strictNullChecks && declaration.flags & NodeFlags.Ambient && isParameterDeclaration(declaration)) { parentType = getNonNullableType(parentType); } - const declaredType = getTypeOfPropertyOfType(parentType, text); + const propType = getTypeOfPropertyOfType(parentType, text); + const declaredType = propType && getApparentTypeForLocation(propType, declaration.name); type = declaredType && getFlowTypeOfReference(declaration, declaredType) || isNumericLiteralName(text) && getIndexTypeOfType(parentType, IndexKind.Number) || getIndexTypeOfType(parentType, IndexKind.String); @@ -11768,16 +11769,6 @@ namespace ts { } function getTypeWithFacts(type: Type, include: TypeFacts) { - if (type.flags & TypeFlags.IndexedAccess) { - // TODO (weswig): This is a substitute for a lazy negated type to remove the types indicated by the TypeFacts from the (potential) union the IndexedAccess refers to - // - See discussion in https://github.com/Microsoft/TypeScript/pull/19275 for details, and test `strictNullNotNullIndexTypeShouldWork` for current behavior - const baseConstraint = getBaseConstraintOfType(type) || emptyObjectType; - const result = filterType(baseConstraint, t => (getTypeFacts(t) & include) !== 0); - if (result !== baseConstraint) { - return result; - } - return type; - } return filterType(type, t => (getTypeFacts(t) & include) !== 0); } @@ -12891,19 +12882,20 @@ namespace ts { const parent = node.parent; return parent.kind === SyntaxKind.PropertyAccessExpression || parent.kind === SyntaxKind.CallExpression && (parent).expression === node || - parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === node; + parent.kind === SyntaxKind.ElementAccessExpression && (parent).expression === node || + parent.kind === SyntaxKind.NonNullExpression || + parent.kind === SyntaxKind.BindingElement && (parent).name === node && !!(parent).initializer; } function typeHasNullableConstraint(type: Type) { return type.flags & TypeFlags.TypeVariable && maybeTypeOfKind(getBaseConstraintOfType(type) || emptyObjectType, TypeFlags.Nullable); } - function getDeclaredOrApparentType(symbol: Symbol, node: Node) { + function getApparentTypeForLocation(type: Type, node: Node) { // When a node is the left hand expression of a property access, element access, or call expression, // and the type of the node includes type variables with constraints that are nullable, we fetch the // apparent type of the node *before* performing control flow analysis such that narrowings apply to // the constraint type. - const type = getTypeOfSymbol(symbol); if (isApparentTypePosition(node) && forEachType(type, typeHasNullableConstraint)) { return mapType(getWidenedType(type), getApparentType); } @@ -12993,7 +12985,7 @@ namespace ts { checkCollisionWithCapturedNewTargetVariable(node, node); checkNestedBlockScopedBinding(node, symbol); - const type = getDeclaredOrApparentType(localOrExportSymbol, node); + const type = getApparentTypeForLocation(getTypeOfSymbol(localOrExportSymbol), node); const assignmentKind = getAssignmentTargetKind(node); if (assignmentKind) { @@ -15559,7 +15551,7 @@ namespace ts { return unknownType; } } - propType = getDeclaredOrApparentType(prop, node); + propType = getApparentTypeForLocation(getTypeOfSymbol(prop), node); } // Only compute control flow type if this is a property access expression that isn't an // assignment target, and the referenced property was declared as a variable, property, diff --git a/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.js b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.js new file mode 100644 index 00000000000..8fcc3efaaf9 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.js @@ -0,0 +1,28 @@ +//// [definiteAssignmentOfDestructuredVariable.ts] +// https://github.com/Microsoft/TypeScript/issues/20994 +interface Options { + a?: number | object; + b: () => void; +} + +class C { + foo!: { [P in keyof T]: T[P] } + + method() { + let { a, b } = this.foo; + !(a && b); + a; + } +} + +//// [definiteAssignmentOfDestructuredVariable.js] +var C = /** @class */ (function () { + function C() { + } + C.prototype.method = function () { + var _a = this.foo, a = _a.a, b = _a.b; + !(a && b); + a; + }; + return C; +}()); diff --git a/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.symbols b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.symbols new file mode 100644 index 00000000000..64726c73de7 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts === +// https://github.com/Microsoft/TypeScript/issues/20994 +interface Options { +>Options : Symbol(Options, Decl(definiteAssignmentOfDestructuredVariable.ts, 0, 0)) + + a?: number | object; +>a : Symbol(Options.a, Decl(definiteAssignmentOfDestructuredVariable.ts, 1, 19)) + + b: () => void; +>b : Symbol(Options.b, Decl(definiteAssignmentOfDestructuredVariable.ts, 2, 24)) +} + +class C { +>C : Symbol(C, Decl(definiteAssignmentOfDestructuredVariable.ts, 4, 1)) +>T : Symbol(T, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 8)) +>Options : Symbol(Options, Decl(definiteAssignmentOfDestructuredVariable.ts, 0, 0)) + + foo!: { [P in keyof T]: T[P] } +>foo : Symbol(C.foo, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 28)) +>P : Symbol(P, Decl(definiteAssignmentOfDestructuredVariable.ts, 7, 13)) +>T : Symbol(T, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 8)) +>T : Symbol(T, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 8)) +>P : Symbol(P, Decl(definiteAssignmentOfDestructuredVariable.ts, 7, 13)) + + method() { +>method : Symbol(C.method, Decl(definiteAssignmentOfDestructuredVariable.ts, 7, 34)) + + let { a, b } = this.foo; +>a : Symbol(a, Decl(definiteAssignmentOfDestructuredVariable.ts, 10, 13)) +>b : Symbol(b, Decl(definiteAssignmentOfDestructuredVariable.ts, 10, 16)) +>this.foo : Symbol(C.foo, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 28)) +>this : Symbol(C, Decl(definiteAssignmentOfDestructuredVariable.ts, 4, 1)) +>foo : Symbol(C.foo, Decl(definiteAssignmentOfDestructuredVariable.ts, 6, 28)) + + !(a && b); +>a : Symbol(a, Decl(definiteAssignmentOfDestructuredVariable.ts, 10, 13)) +>b : Symbol(b, Decl(definiteAssignmentOfDestructuredVariable.ts, 10, 16)) + + a; +>a : Symbol(a, Decl(definiteAssignmentOfDestructuredVariable.ts, 10, 13)) + } +} diff --git a/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types new file mode 100644 index 00000000000..36ff5028305 --- /dev/null +++ b/tests/baselines/reference/definiteAssignmentOfDestructuredVariable.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts === +// https://github.com/Microsoft/TypeScript/issues/20994 +interface Options { +>Options : Options + + a?: number | object; +>a : number | object | undefined + + b: () => void; +>b : () => void +} + +class C { +>C : C +>T : T +>Options : Options + + foo!: { [P in keyof T]: T[P] } +>foo : { [P in keyof T]: T[P]; } +>P : P +>T : T +>T : T +>P : P + + method() { +>method : () => void + + let { a, b } = this.foo; +>a : T["a"] +>b : T["b"] +>this.foo : { [P in keyof T]: T[P]; } +>this : this +>foo : { [P in keyof T]: T[P]; } + + !(a && b); +>!(a && b) : false +>(a && b) : T["b"] +>a && b : T["b"] +>a : T["a"] +>b : T["b"] + + a; +>a : T["a"] + } +} diff --git a/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types b/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types index 76515fbdd53..e401a3d4950 100644 --- a/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types +++ b/tests/baselines/reference/strictNullNotNullIndexTypeShouldWork.types @@ -23,11 +23,11 @@ class Test { this.attrs.params!.name; >this.attrs.params!.name : string >this.attrs.params! : { name: string; } ->this.attrs.params : T["params"] +>this.attrs.params : { name: string; } | undefined >this.attrs : Readonly >this : this >attrs : Readonly ->params : T["params"] +>params : { name: string; } | undefined >name : string } } @@ -80,10 +80,10 @@ class Test2 { return this.attrs.params!; // Return type should maintain relationship with `T` after being not-null-asserted, ideally >this.attrs.params! : { name: string; } ->this.attrs.params : T["params"] +>this.attrs.params : { name: string; } | undefined >this.attrs : Readonly >this : this >attrs : Readonly ->params : T["params"] +>params : { name: string; } | undefined } } diff --git a/tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts b/tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts new file mode 100644 index 00000000000..800a9df8313 --- /dev/null +++ b/tests/cases/compiler/definiteAssignmentOfDestructuredVariable.ts @@ -0,0 +1,16 @@ +// @strictNullChecks: true +// https://github.com/Microsoft/TypeScript/issues/20994 +interface Options { + a?: number | object; + b: () => void; +} + +class C { + foo!: { [P in keyof T]: T[P] } + + method() { + let { a, b } = this.foo; + !(a && b); + a; + } +} \ No newline at end of file From d1ff12e0a69b5ddd7bd84b37824e85afb2c12302 Mon Sep 17 00:00:00 2001 From: Wenlu Wang <805037171@163.com> Date: Sat, 20 Jan 2018 09:26:58 +0800 Subject: [PATCH 067/100] add completion filter for function like body (#21257) --- src/services/completions.ts | 43 +++++++++++++++++++ .../fourslash/completionInFunctionLikeBody.ts | 43 +++++++++++++++++++ .../fourslash/completionInJSDocFunctionNew.ts | 2 +- .../completionInJSDocFunctionThis.ts | 2 +- 4 files changed, 88 insertions(+), 2 deletions(-) create mode 100644 tests/cases/fourslash/completionInFunctionLikeBody.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 82e7065228a..80fdf6c280e 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -20,6 +20,7 @@ namespace ts.Completions { None, ClassElementKeywords, // Keywords at class keyword ConstructorParameterKeywords, // Keywords at constructor parameter + FunctionLikeBodyKeywords // Keywords at function like body } export function getCompletionsAtPosition( @@ -1060,6 +1061,10 @@ namespace ts.Completions { return true; } + if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { + keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords; + } + if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { // cursor inside class declaration getGetClassLikeCompletionSymbols(classLikeContainer); @@ -1688,6 +1693,22 @@ namespace ts.Completions { return undefined; } + function tryGetFunctionLikeBodyCompletionContainer(contextToken: Node): FunctionLikeDeclaration { + if (contextToken) { + let prev: Node; + const container = findAncestor(contextToken.parent, (node: Node) => { + if (isClassLike(node)) { + return "quit"; + } + if (isFunctionLikeDeclaration(node) && prev === node.body) { + return true; + } + prev = node; + }); + return container && container as FunctionLikeDeclaration; + } + } + function tryGetContainingJsxElement(contextToken: Node): JsxOpeningLikeElement { if (contextToken) { const parent = contextToken.parent; @@ -2126,6 +2147,8 @@ namespace ts.Completions { return getFilteredKeywordCompletions(isClassMemberCompletionKeywordText); case KeywordCompletionFilters.ConstructorParameterKeywords: return getFilteredKeywordCompletions(isConstructorParameterCompletionKeywordText); + case KeywordCompletionFilters.FunctionLikeBodyKeywords: + return getFilteredKeywordCompletions(isFunctionLikeBodyCompletionKeywordText); default: Debug.assertNever(keywordFilter); } @@ -2188,6 +2211,26 @@ namespace ts.Completions { return isConstructorParameterCompletionKeyword(stringToToken(text)); } + function isFunctionLikeBodyCompletionKeyword(kind: SyntaxKind) { + switch (kind) { + case SyntaxKind.PublicKeyword: + case SyntaxKind.PrivateKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.ReadonlyKeyword: + case SyntaxKind.ConstructorKeyword: + case SyntaxKind.StaticKeyword: + case SyntaxKind.AbstractKeyword: + case SyntaxKind.GetKeyword: + case SyntaxKind.SetKeyword: + return false; + } + return true; + } + + function isFunctionLikeBodyCompletionKeywordText(text: string) { + return isFunctionLikeBodyCompletionKeyword(stringToToken(text)); + } + function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { switch (kind) { case ts.SyntaxKind.EqualsEqualsEqualsToken: diff --git a/tests/cases/fourslash/completionInFunctionLikeBody.ts b/tests/cases/fourslash/completionInFunctionLikeBody.ts new file mode 100644 index 00000000000..a4c5e92dde9 --- /dev/null +++ b/tests/cases/fourslash/completionInFunctionLikeBody.ts @@ -0,0 +1,43 @@ +/// + +//// class Foo { +//// bar () { +//// /*1*/ +//// class Foo1 { +//// bar1 () { +//// /*2*/ +//// } +//// /*3*/ +//// } +//// } +//// /*4*/ +//// } + + +goTo.marker("1"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("2"); +verify.not.completionListContains("public", "public", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("private", "private", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("protected", "protected", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("constructor", "constructor", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("readonly", "readonly", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("static", "static", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("abstract", "abstract", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("get", "get", /*documentation*/ undefined, "keyword"); +verify.not.completionListContains("set", "set", /*documentation*/ undefined, "keyword"); + +goTo.marker("3"); +verify.completionListContainsClassElementKeywords(); + +goTo.marker("4"); +verify.completionListContainsClassElementKeywords(); diff --git a/tests/cases/fourslash/completionInJSDocFunctionNew.ts b/tests/cases/fourslash/completionInJSDocFunctionNew.ts index 6a06ec76fe5..1266eb5fad3 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionNew.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionNew.ts @@ -6,5 +6,5 @@ ////var f = function () { return new/**/; } goTo.marker(); -verify.completionListCount(116); +verify.completionListCount(107); verify.completionListContains('new'); diff --git a/tests/cases/fourslash/completionInJSDocFunctionThis.ts b/tests/cases/fourslash/completionInJSDocFunctionThis.ts index 0fd771f272b..57cf08c9ff3 100644 --- a/tests/cases/fourslash/completionInJSDocFunctionThis.ts +++ b/tests/cases/fourslash/completionInJSDocFunctionThis.ts @@ -5,5 +5,5 @@ ////var f = function (s) { return this/**/; } goTo.marker(); -verify.completionListCount(117); +verify.completionListCount(108); verify.completionListContains('this') From a7c53c70d2d74520483a892eb3e9ca55daf655cf Mon Sep 17 00:00:00 2001 From: Philippe Voinov Date: Sat, 20 Jan 2018 02:32:37 +0100 Subject: [PATCH 068/100] Fix isTypeOfExpression in compiler API (#20875). (#20884) --- src/compiler/utilities.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 6a421bf6948..bf6a086967c 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -4704,7 +4704,7 @@ namespace ts { } export function isTypeOfExpression(node: Node): node is TypeOfExpression { - return node.kind === SyntaxKind.AwaitExpression; + return node.kind === SyntaxKind.TypeOfExpression; } export function isVoidExpression(node: Node): node is VoidExpression { From 92bde084c1e5ce42ac9491273dec3e846fc61c7e Mon Sep 17 00:00:00 2001 From: Esakki Raj Date: Sun, 21 Jan 2018 23:15:58 +0530 Subject: [PATCH 069/100] Fix formatting between for and await (#21254) * Fix issue 21084 * Removed unwanted rules. --- src/services/formatting/rules.ts | 3 +++ tests/cases/fourslash/formattingAwait.ts | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/cases/fourslash/formattingAwait.ts diff --git a/src/services/formatting/rules.ts b/src/services/formatting/rules.ts index 16270eda479..f6a9dee4cfe 100644 --- a/src/services/formatting/rules.ts +++ b/src/services/formatting/rules.ts @@ -321,6 +321,9 @@ namespace ts.formatting { rule("NoSpaceAfterCloseBracket", SyntaxKind.CloseBracketToken, anyToken, [isNonJsxSameLineTokenContext, isNotBeforeBlockInFunctionDeclarationContext], RuleAction.Delete), rule("SpaceAfterSemicolon", SyntaxKind.SemicolonToken, anyToken, [isNonJsxSameLineTokenContext], RuleAction.Space), + // Remove extra space between for and await + rule("SpaceBetweenForAndAwaitKeyword", SyntaxKind.ForKeyword, SyntaxKind.AwaitKeyword, [isNonJsxSameLineTokenContext], RuleAction.Space), + // Add a space between statements. All keywords except (do,else,case) has open/close parens after them. // So, we have a rule to add a space for [),Any], [do,Any], [else,Any], and [case,Any] rule( diff --git a/tests/cases/fourslash/formattingAwait.ts b/tests/cases/fourslash/formattingAwait.ts new file mode 100644 index 00000000000..e6a86f92d08 --- /dev/null +++ b/tests/cases/fourslash/formattingAwait.ts @@ -0,0 +1,18 @@ +/// + +////async function f() { +//// for await (const x of g()) { +//// console.log(x); +//// } +////} + + +format.document(); + +verify.currentFileContentIs( +`async function f() { + for await (const x of g()) { + console.log(x); + } +}` +); \ No newline at end of file From 18e1ac030613bd15a55156bd95e16b5002e99ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Ginth=C3=B6r?= <26004708+Lazarus535@users.noreply.github.com> Date: Mon, 22 Jan 2018 09:52:58 +0100 Subject: [PATCH 070/100] Fixes #17080 Changes are in src/compiler.checker.ts only The second arguments to the function "removeOptionalityFromDeclaredType" has been changed from "getRootDeclaration(declaration)" to "declaration". --- pull_request_template.md | 9 +- src/compiler/checker.ts | 2 +- ...rInDestructuringWithInitializer.errors.txt | 62 ++++++ ...ParameterInDestructuringWithInitializer.js | 80 +++++++ ...eterInDestructuringWithInitializer.symbols | 154 ++++++++++++++ ...ameterInDestructuringWithInitializer.types | 198 ++++++++++++++++++ ...ParameterInDestructuringWithInitializer.ts | 39 ++++ 7 files changed, 539 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt create mode 100644 tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js create mode 100644 tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols create mode 100644 tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types create mode 100644 tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts diff --git a/pull_request_template.md b/pull_request_template.md index 2c49c84641b..faa484054be 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -2,15 +2,16 @@ Thank you for submitting a pull request! Here's a checklist you might find useful. -[ ] There is an associated issue that is labelled +[X] There is an associated issue that is labelled 'Bug' or 'help wanted' or is in the Community milestone -[ ] Code is up-to-date with the `master` branch -[ ] You've successfully run `jake runtests` locally +[X] Code is up-to-date with the `master` branch +[X] You've successfully run `jake runtests` locally [ ] You've signed the CLA -[ ] There are new or updated unit tests validating the change +[X] There are new or updated unit tests validating the change Refer to CONTRIBUTING.MD for more details. https://github.com/Microsoft/TypeScript/blob/master/CONTRIBUTING.md --> Fixes # +#17080 diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 405fd11f138..74c60bcdb62 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13046,7 +13046,7 @@ namespace ts { node.parent.kind === SyntaxKind.NonNullExpression || declaration.kind === SyntaxKind.VariableDeclaration && (declaration).exclamationToken || declaration.flags & NodeFlags.Ambient; - const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, getRootDeclaration(declaration) as VariableLikeDeclaration) : type) : + const initialType = assumeInitialized ? (isParameter ? removeOptionalityFromDeclaredType(type, declaration as VariableLikeDeclaration) : type) : type === autoType || type === autoArrayType ? undefinedType : getOptionalType(type); const flowType = getFlowTypeOfReference(node, type, initialType, flowContainer, !assumeInitialized); diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt new file mode 100644 index 00000000000..14bc3ea8e6c --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt @@ -0,0 +1,62 @@ +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(6,8): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(16,7): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(21,7): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(31,8): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. + + +==== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts (4 errors) ==== + // https://github.com/Microsoft/TypeScript/issues/17080 + function f(a:number,b:number) { + } + + function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { + f(a, b) + ~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + // error + } + + function func2( {a, b = 3}: {a: number, b?:number} = {a: 1,b: 2} ) { + f(a, b) + // no error + } + + function func3( {a, b}: {a: number, b?: number} = {a: 1} ) { + f(a,b) + ~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + // error + } + + function func4( {a: {b, c}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b,c) + ~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + // error + } + + function func5({a: {b, c = 4}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b, c) + // no error + } + + function func6( {a: {b, c} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1,c: 2}, d: 3} ) { + f(b, c) + ~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + // error + } + + function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1, c: 2}, d: 3} ) { + f(b, c) + // no error + } + \ No newline at end of file diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js new file mode 100644 index 00000000000..8c297088f14 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js @@ -0,0 +1,80 @@ +//// [optionalParameterInDestructuringWithInitializer.ts] +// https://github.com/Microsoft/TypeScript/issues/17080 +function f(a:number,b:number) { +} + +function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { + f(a, b) + // error +} + +function func2( {a, b = 3}: {a: number, b?:number} = {a: 1,b: 2} ) { + f(a, b) + // no error +} + +function func3( {a, b}: {a: number, b?: number} = {a: 1} ) { + f(a,b) + // error +} + +function func4( {a: {b, c}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b,c) + // error +} + +function func5({a: {b, c = 4}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b, c) + // no error +} + +function func6( {a: {b, c} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1,c: 2}, d: 3} ) { + f(b, c) + // error +} + +function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1, c: 2}, d: 3} ) { + f(b, c) + // no error +} + + +//// [optionalParameterInDestructuringWithInitializer.js] +// https://github.com/Microsoft/TypeScript/issues/17080 +function f(a, b) { +} +function func1(_a) { + var _b = _a === void 0 ? { a: 1, b: 2 } : _a, a = _b.a, b = _b.b; + f(a, b); + // error +} +function func2(_a) { + var _b = _a === void 0 ? { a: 1, b: 2 } : _a, a = _b.a, _c = _b.b, b = _c === void 0 ? 3 : _c; + f(a, b); + // no error +} +function func3(_a) { + var _b = _a === void 0 ? { a: 1 } : _a, a = _b.a, b = _b.b; + f(a, b); + // error +} +function func4(_a) { + var _b = _a === void 0 ? { a: { b: 1, c: 2 }, d: 3 } : _a, _c = _b.a, b = _c.b, c = _c.c, d = _b.d; + f(b, c); + // error +} +function func5(_a) { + var _b = _a === void 0 ? { a: { b: 1, c: 2 }, d: 3 } : _a, _c = _b.a, b = _c.b, _d = _c.c, c = _d === void 0 ? 4 : _d, d = _b.d; + f(b, c); + // no error +} +function func6(_a) { + var _b = _a === void 0 ? { a: { b: 1, c: 2 }, d: 3 } : _a, _c = _b.a, _d = _c === void 0 ? { b: 4, c: 5 } : _c, b = _d.b, c = _d.c, d = _b.d; + f(b, c); + // error +} +function func7(_a) { + var _b = _a === void 0 ? { a: { b: 1, c: 2 }, d: 3 } : _a, _c = _b.a, _d = _c === void 0 ? { b: 4, c: 5 } : _c, b = _d.b, _e = _d.c, c = _e === void 0 ? 6 : _e, d = _b.d; + f(b, c); + // no error +} diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols new file mode 100644 index 00000000000..fc068a0babb --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols @@ -0,0 +1,154 @@ +=== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === +// https://github.com/Microsoft/TypeScript/issues/17080 +function f(a:number,b:number) { +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 1, 11)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 1, 20)) +} + +function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { +>func1 : Symbol(func1, Decl(optionalParameterInDestructuringWithInitializer.ts, 2, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 19)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 25)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 35)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 51)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 56)) + + f(a, b) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 19)) + + // error +} + +function func2( {a, b = 3}: {a: number, b?:number} = {a: 1,b: 2} ) { +>func2 : Symbol(func2, Decl(optionalParameterInDestructuringWithInitializer.ts, 7, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 19)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 29)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 39)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 54)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 59)) + + f(a, b) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 9, 19)) + + // no error +} + +function func3( {a, b}: {a: number, b?: number} = {a: 1} ) { +>func3 : Symbol(func3, Decl(optionalParameterInDestructuringWithInitializer.ts, 12, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 19)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 25)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 35)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 51)) + + f(a,b) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 17)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 14, 19)) + + // error +} + +function func4( {a: {b, c}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { +>func4 : Symbol(func4, Decl(optionalParameterInDestructuringWithInitializer.ts, 17, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 33)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 23)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 27)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 33)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 37)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 47)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 59)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 73)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 77)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 82)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 88)) + + f(b,c) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 19, 23)) + + // error +} + +function func5({a: {b, c = 4}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { +>func5 : Symbol(func5, Decl(optionalParameterInDestructuringWithInitializer.ts, 22, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 36)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 20)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 22)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 30)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 36)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 40)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 50)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 62)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 76)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 80)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 85)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 91)) + + f(b, c) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 20)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 24, 22)) + + // no error +} + +function func6( {a: {b, c} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1,c: 2}, d: 3} ) { +>func6 : Symbol(func6, Decl(optionalParameterInDestructuringWithInitializer.ts, 27, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 48)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 23)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 30)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 35)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 42)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 48)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 52)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 62)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 75)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 90)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 94)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 99)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 105)) + + f(b, c) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 29, 23)) + + // error +} + +function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1, c: 2}, d: 3} ) { +>func7 : Symbol(func7, Decl(optionalParameterInDestructuringWithInitializer.ts, 32, 1)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 52)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 23)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 34)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 39)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 46)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 52)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 56)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 66)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 79)) +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 94)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 98)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 103)) +>d : Symbol(d, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 110)) + + f(b, c) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 21)) +>c : Symbol(c, Decl(optionalParameterInDestructuringWithInitializer.ts, 34, 23)) + + // no error +} + diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types new file mode 100644 index 00000000000..8def19dde20 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types @@ -0,0 +1,198 @@ +=== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === +// https://github.com/Microsoft/TypeScript/issues/17080 +function f(a:number,b:number) { +>f : (a: number, b: number) => void +>a : number +>b : number +} + +function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { +>func1 : ({ a, b }?: { a: number; b?: number | undefined; }) => void +>a : number +>b : number | undefined +>a : number +>b : number | undefined +>{a: 1, b: 2} : { a: number; b: number; } +>a : number +>1 : 1 +>b : number +>2 : 2 + + f(a, b) +>f(a, b) : void +>f : (a: number, b: number) => void +>a : number +>b : number | undefined + + // error +} + +function func2( {a, b = 3}: {a: number, b?:number} = {a: 1,b: 2} ) { +>func2 : ({ a, b }?: { a: number; b?: number | undefined; }) => void +>a : number +>b : number +>3 : 3 +>a : number +>b : number | undefined +>{a: 1,b: 2} : { a: number; b: number; } +>a : number +>1 : 1 +>b : number +>2 : 2 + + f(a, b) +>f(a, b) : void +>f : (a: number, b: number) => void +>a : number +>b : number + + // no error +} + +function func3( {a, b}: {a: number, b?: number} = {a: 1} ) { +>func3 : ({ a, b }?: { a: number; b?: number | undefined; }) => void +>a : number +>b : number | undefined +>a : number +>b : number | undefined +>{a: 1} : { a: number; } +>a : number +>1 : 1 + + f(a,b) +>f(a,b) : void +>f : (a: number, b: number) => void +>a : number +>b : number | undefined + + // error +} + +function func4( {a: {b, c}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { +>func4 : ({ a: { b, c }, d }?: { a: { b: number; c?: number | undefined; }; d: number; }) => void +>a : any +>b : number +>c : number | undefined +>d : number +>a : { b: number; c?: number | undefined; } +>b : number +>c : number | undefined +>d : number +>{a: {b: 1,c: 2},d: 3} : { a: { b: number; c: number; }; d: number; } +>a : { b: number; c: number; } +>{b: 1,c: 2} : { b: number; c: number; } +>b : number +>1 : 1 +>c : number +>2 : 2 +>d : number +>3 : 3 + + f(b,c) +>f(b,c) : void +>f : (a: number, b: number) => void +>b : number +>c : number | undefined + + // error +} + +function func5({a: {b, c = 4}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { +>func5 : ({ a: { b, c }, d }?: { a: { b: number; c?: number | undefined; }; d: number; }) => void +>a : any +>b : number +>c : number +>4 : 4 +>d : number +>a : { b: number; c?: number | undefined; } +>b : number +>c : number | undefined +>d : number +>{a: {b: 1,c: 2},d: 3} : { a: { b: number; c: number; }; d: number; } +>a : { b: number; c: number; } +>{b: 1,c: 2} : { b: number; c: number; } +>b : number +>1 : 1 +>c : number +>2 : 2 +>d : number +>3 : 3 + + f(b, c) +>f(b, c) : void +>f : (a: number, b: number) => void +>b : number +>c : number + + // no error +} + +function func6( {a: {b, c} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1,c: 2}, d: 3} ) { +>func6 : ({ a: { b, c }, d }?: { a: { b: number; c?: number | undefined; }; d: number; }) => void +>a : any +>b : number +>c : number | undefined +>{b: 4, c: 5} : { b: number; c: number; } +>b : number +>4 : 4 +>c : number +>5 : 5 +>d : number +>a : { b: number; c?: number | undefined; } +>b : number +>c : number | undefined +>d : number +>{a: {b: 1,c: 2}, d: 3} : { a: { b: number; c: number; }; d: number; } +>a : { b: number; c: number; } +>{b: 1,c: 2} : { b: number; c: number; } +>b : number +>1 : 1 +>c : number +>2 : 2 +>d : number +>3 : 3 + + f(b, c) +>f(b, c) : void +>f : (a: number, b: number) => void +>b : number +>c : number | undefined + + // error +} + +function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1, c: 2}, d: 3} ) { +>func7 : ({ a: { b, c }, d }?: { a: { b: number; c?: number | undefined; }; d: number; }) => void +>a : any +>b : number +>c : number +>6 : 6 +>{b: 4, c: 5} : { b: number; c?: number; } +>b : number +>4 : 4 +>c : number +>5 : 5 +>d : number +>a : { b: number; c?: number | undefined; } +>b : number +>c : number | undefined +>d : number +>{a: {b: 1, c: 2}, d: 3} : { a: { b: number; c: number; }; d: number; } +>a : { b: number; c: number; } +>{b: 1, c: 2} : { b: number; c: number; } +>b : number +>1 : 1 +>c : number +>2 : 2 +>d : number +>3 : 3 + + f(b, c) +>f(b, c) : void +>f : (a: number, b: number) => void +>b : number +>c : number + + // no error +} + diff --git a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts new file mode 100644 index 00000000000..3666ee2d954 --- /dev/null +++ b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts @@ -0,0 +1,39 @@ +// @strictNullChecks: true +// https://github.com/Microsoft/TypeScript/issues/17080 +function f(a:number,b:number) { +} + +function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { + f(a, b) + // error +} + +function func2( {a, b = 3}: {a: number, b?:number} = {a: 1,b: 2} ) { + f(a, b) + // no error +} + +function func3( {a, b}: {a: number, b?: number} = {a: 1} ) { + f(a,b) + // error +} + +function func4( {a: {b, c}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b,c) + // error +} + +function func5({a: {b, c = 4}, d}: {a: {b: number,c?: number},d: number} = {a: {b: 1,c: 2},d: 3} ) { + f(b, c) + // no error +} + +function func6( {a: {b, c} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1,c: 2}, d: 3} ) { + f(b, c) + // error +} + +function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, d: number} = {a: {b: 1, c: 2}, d: 3} ) { + f(b, c) + // no error +} From 5a87a94c59a7ea137113a873b163c98e7322fa81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Ginth=C3=B6r?= <26004708+Lazarus535@users.noreply.github.com> Date: Mon, 22 Jan 2018 13:40:57 +0100 Subject: [PATCH 071/100] Fixes #17080 https://github.com/Microsoft/TypeScript/issues/17080 Added testcases from the Github bugreport (all working as intended now). Signed CLA. --- pull_request_template.md | 2 +- ...rInDestructuringWithInitializer.errors.txt | 38 ++++++++++- ...ParameterInDestructuringWithInitializer.js | 44 +++++++++++++ ...eterInDestructuringWithInitializer.symbols | 55 ++++++++++++++++ ...ameterInDestructuringWithInitializer.types | 66 +++++++++++++++++++ ...ParameterInDestructuringWithInitializer.ts | 26 ++++++++ 6 files changed, 229 insertions(+), 2 deletions(-) diff --git a/pull_request_template.md b/pull_request_template.md index faa484054be..4243c2f671f 100644 --- a/pull_request_template.md +++ b/pull_request_template.md @@ -6,7 +6,7 @@ Here's a checklist you might find useful. 'Bug' or 'help wanted' or is in the Community milestone [X] Code is up-to-date with the `master` branch [X] You've successfully run `jake runtests` locally -[ ] You've signed the CLA +[X] You've signed the CLA [X] There are new or updated unit tests validating the change Refer to CONTRIBUTING.MD for more details. diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt index 14bc3ea8e6c..8965d7172e8 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt @@ -6,9 +6,13 @@ tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(21,7): e Type 'undefined' is not assignable to type 'number'. tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(31,8): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. Type 'undefined' is not assignable to type 'number'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(45,10): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. + Type 'undefined' is not assignable to type 'number'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(55,11): error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number | undefined'. + Type 'null' is not assignable to type 'number | undefined'. -==== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts (4 errors) ==== +==== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts (6 errors) ==== // https://github.com/Microsoft/TypeScript/issues/17080 function f(a:number,b:number) { } @@ -59,4 +63,36 @@ tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(31,8): e f(b, c) // no error } + + interface Foo { + readonly bar?: number; + } + + function performFoo({ bar }: Foo = {}) { + useBar(bar); + ~~~ +!!! error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. +!!! error TS2345: Type 'undefined' is not assignable to type 'number'. + } + + function useBar(bar: number) { + f(bar, 1) + } + + performFoo(); + + function performFoo2({ bar = null }: Foo = {}) { + useBar2(bar); + ~~~ +!!! error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number | undefined'. +!!! error TS2345: Type 'null' is not assignable to type 'number | undefined'. + } + + function useBar2(bar: number | undefined) { + if (bar) { + f(bar, 1) + } + } + + performFoo2(); \ No newline at end of file diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js index 8c297088f14..cb929d89112 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js @@ -37,6 +37,32 @@ function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, f(b, c) // no error } + +interface Foo { + readonly bar?: number; +} + +function performFoo({ bar }: Foo = {}) { + useBar(bar); +} + +function useBar(bar: number) { + f(bar, 1) +} + +performFoo(); + +function performFoo2({ bar = null }: Foo = {}) { + useBar2(bar); +} + +function useBar2(bar: number | undefined) { + if (bar) { + f(bar, 1) + } +} + +performFoo2(); //// [optionalParameterInDestructuringWithInitializer.js] @@ -78,3 +104,21 @@ function func7(_a) { f(b, c); // no error } +function performFoo(_a) { + var bar = (_a === void 0 ? {} : _a).bar; + useBar(bar); +} +function useBar(bar) { + f(bar, 1); +} +performFoo(); +function performFoo2(_a) { + var _b = (_a === void 0 ? {} : _a).bar, bar = _b === void 0 ? null : _b; + useBar2(bar); +} +function useBar2(bar) { + if (bar) { + f(bar, 1); + } +} +performFoo2(); diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols index fc068a0babb..399956476c8 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols @@ -152,3 +152,58 @@ function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, // no error } +interface Foo { +>Foo : Symbol(Foo, Decl(optionalParameterInDestructuringWithInitializer.ts, 37, 1)) + + readonly bar?: number; +>bar : Symbol(Foo.bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 39, 15)) +} + +function performFoo({ bar }: Foo = {}) { +>performFoo : Symbol(performFoo, Decl(optionalParameterInDestructuringWithInitializer.ts, 41, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 43, 21)) +>Foo : Symbol(Foo, Decl(optionalParameterInDestructuringWithInitializer.ts, 37, 1)) + + useBar(bar); +>useBar : Symbol(useBar, Decl(optionalParameterInDestructuringWithInitializer.ts, 45, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 43, 21)) +} + +function useBar(bar: number) { +>useBar : Symbol(useBar, Decl(optionalParameterInDestructuringWithInitializer.ts, 45, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 47, 16)) + + f(bar, 1) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 47, 16)) +} + +performFoo(); +>performFoo : Symbol(performFoo, Decl(optionalParameterInDestructuringWithInitializer.ts, 41, 1)) + +function performFoo2({ bar = null }: Foo = {}) { +>performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 13)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 22)) +>Foo : Symbol(Foo, Decl(optionalParameterInDestructuringWithInitializer.ts, 37, 1)) + + useBar2(bar); +>useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 55, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 22)) +} + +function useBar2(bar: number | undefined) { +>useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 55, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) + + if (bar) { +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) + + f(bar, 1) +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) + } +} + +performFoo2(); +>performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 13)) + diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types index 8def19dde20..1d1618e12e3 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types @@ -196,3 +196,69 @@ function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, // no error } +interface Foo { +>Foo : Foo + + readonly bar?: number; +>bar : number | undefined +} + +function performFoo({ bar }: Foo = {}) { +>performFoo : ({ bar }?: Foo) => void +>bar : number | undefined +>Foo : Foo +>{} : {} + + useBar(bar); +>useBar(bar) : void +>useBar : (bar: number) => void +>bar : number | undefined +} + +function useBar(bar: number) { +>useBar : (bar: number) => void +>bar : number + + f(bar, 1) +>f(bar, 1) : void +>f : (a: number, b: number) => void +>bar : number +>1 : 1 +} + +performFoo(); +>performFoo() : void +>performFoo : ({ bar }?: Foo) => void + +function performFoo2({ bar = null }: Foo = {}) { +>performFoo2 : ({ bar }?: Foo) => void +>bar : number | null +>null : null +>Foo : Foo +>{} : {} + + useBar2(bar); +>useBar2(bar) : void +>useBar2 : (bar: number | undefined) => void +>bar : number | null +} + +function useBar2(bar: number | undefined) { +>useBar2 : (bar: number | undefined) => void +>bar : number | undefined + + if (bar) { +>bar : number | undefined + + f(bar, 1) +>f(bar, 1) : void +>f : (a: number, b: number) => void +>bar : number +>1 : 1 + } +} + +performFoo2(); +>performFoo2() : void +>performFoo2 : ({ bar }?: Foo) => void + diff --git a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts index 3666ee2d954..1f1847a19a7 100644 --- a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts +++ b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts @@ -37,3 +37,29 @@ function func7( {a: {b, c = 6} = {b: 4, c: 5}, d}: {a: {b: number, c?: number}, f(b, c) // no error } + +interface Foo { + readonly bar?: number; +} + +function performFoo({ bar }: Foo = {}) { + useBar(bar); +} + +function useBar(bar: number) { + f(bar, 1) +} + +performFoo(); + +function performFoo2({ bar = null }: Foo = {}) { + useBar2(bar); +} + +function useBar2(bar: number | undefined) { + if (bar) { + f(bar, 1) + } +} + +performFoo2(); From 588716926dcaa063aecb4d64232e2f3cfd8e23b2 Mon Sep 17 00:00:00 2001 From: Andy Date: Mon, 22 Jan 2018 10:15:57 -0800 Subject: [PATCH 072/100] Fix bug: result of createUnionOrIntersectionProperty may be undefined (#21332) --- src/compiler/checker.ts | 6 ++++-- tests/cases/fourslash/completionsUnion.ts | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 405fd11f138..14631fa69cb 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5995,7 +5995,9 @@ namespace ts { for (const memberType of types) { for (const { escapedName } of getAugmentedPropertiesOfType(memberType)) { if (!props.has(escapedName)) { - props.set(escapedName, createUnionOrIntersectionProperty(unionType as UnionType, escapedName)); + const prop = createUnionOrIntersectionProperty(unionType as UnionType, escapedName); + // May be undefined if the property is private + if (prop) props.set(escapedName, prop); } } } @@ -6177,7 +6179,7 @@ namespace ts { t; } - function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol { + function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined { let props: Symbol[]; const isUnion = containingType.flags & TypeFlags.Union; const excludeModifiers = isUnion ? ModifierFlags.NonPublicAccessibilityModifier : 0; diff --git a/tests/cases/fourslash/completionsUnion.ts b/tests/cases/fourslash/completionsUnion.ts index ed449936680..d9d1d3c792f 100644 --- a/tests/cases/fourslash/completionsUnion.ts +++ b/tests/cases/fourslash/completionsUnion.ts @@ -2,7 +2,9 @@ ////interface I { x: number; } ////interface Many extends ReadonlyArray { extra: number; } -////const x: I | I[] | Many = { /**/ }; +////class C { private priv: number; } +////const x: I | I[] | Many | C = { /**/ }; // We specifically filter out any array-like types. +// Private members will be excluded by `createUnionOrIntersectionProperty`. verify.completionsAt("", ["x"]); From 146256b7dc2590f067ea8ccd916d1192ef169429 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Wed, 17 Jan 2018 15:35:17 -0800 Subject: [PATCH 073/100] Allow dynamic files without external project and also use file names starting with ^ as dynamic file Fixes #21204 --- .../unittests/tsserverProjectSystem.ts | 39 +++++++++++++++++++ src/server/editorServices.ts | 4 +- src/server/scriptInfo.ts | 2 +- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/harness/unittests/tsserverProjectSystem.ts b/src/harness/unittests/tsserverProjectSystem.ts index af69e35ec18..fee21fcdb2d 100644 --- a/src/harness/unittests/tsserverProjectSystem.ts +++ b/src/harness/unittests/tsserverProjectSystem.ts @@ -2846,6 +2846,45 @@ namespace ts.projectSystem { const options = project.getCompilerOptions(); assert.equal(options.outDir, "C:/a/b", ""); }); + + it("dynamic file without external project", () => { + const file: FileOrFolder = { + path: "^walkThroughSnippet:/Users/UserName/projects/someProject/out/someFile#1.js", + content: "var x = 10;" + }; + const host = createServerHost([libFile], { useCaseSensitiveFileNames: true }); + const projectService = createProjectService(host); + projectService.setCompilerOptionsForInferredProjects({ + module: ModuleKind.CommonJS, + allowJs: true, + allowSyntheticDefaultImports: true, + allowNonTsExtensions: true + }); + projectService.openClientFile(file.path, "var x = 10;"); + + projectService.checkNumberOfProjects({ inferredProjects: 1 }); + const project = projectService.inferredProjects[0]; + checkProjectRootFiles(project, [file.path]); + checkProjectActualFiles(project, [file.path, libFile.path]); + + assert.strictEqual(projectService.getDefaultProjectForFile(server.toNormalizedPath(file.path), /*ensureProject*/ true), project); + const indexOfX = file.content.indexOf("x"); + assert.deepEqual(project.getLanguageService(/*ensureSynchronized*/ true).getQuickInfoAtPosition(file.path, indexOfX), { + kind: ScriptElementKind.variableElement, + kindModifiers: "", + textSpan: { start: indexOfX, length: 1 }, + displayParts: [ + { text: "var", kind: "keyword" }, + { text: " ", kind: "space" }, + { text: "x", kind: "localName" }, + { text: ":", kind: "punctuation" }, + { text: " ", kind: "space" }, + { text: "number", kind: "keyword" } + ], + documentation: [], + tags: [] + }); + }); }); describe("tsserverProjectSystem Proper errors", () => { diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 575e00c5b0e..5d71ff4c2d3 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -898,7 +898,7 @@ namespace ts.server { const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(info, projectRootPath) || this.getOrCreateSingleInferredProjectIfEnabled() || - this.createInferredProject(getDirectoryPath(info.path)); + this.createInferredProject(info.isDynamic ? this.currentDirectory : getDirectoryPath(info.path)); project.addRoot(info); project.updateGraph(); @@ -1655,7 +1655,7 @@ namespace ts.server { } private getOrCreateInferredProjectForProjectRootPathIfEnabled(info: ScriptInfo, projectRootPath: NormalizedPath | undefined): InferredProject | undefined { - if (!this.useInferredProjectPerProjectRoot) { + if (info.isDynamic || !this.useInferredProjectPerProjectRoot) { return undefined; } diff --git a/src/server/scriptInfo.ts b/src/server/scriptInfo.ts index dbadf5c88e7..6fc4f241f65 100644 --- a/src/server/scriptInfo.ts +++ b/src/server/scriptInfo.ts @@ -196,7 +196,7 @@ namespace ts.server { /*@internal*/ export function isDynamicFileName(fileName: NormalizedPath) { - return getBaseFileName(fileName)[0] === "^"; + return fileName[0] === "^" || getBaseFileName(fileName)[0] === "^"; } export class ScriptInfo { From d11341820e3aa3d99c918700a113f2819d8c381e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Ginth=C3=B6r?= <26004708+Lazarus535@users.noreply.github.com> Date: Mon, 22 Jan 2018 21:45:37 +0100 Subject: [PATCH 074/100] Fixes #17080 Fixed the two requested changes. 1) Deleting the file "pull_request_template.md" 2) Declaring functions in tests, instead of defining --- pull_request_template.md | 17 ------- ...rInDestructuringWithInitializer.errors.txt | 16 +++---- ...ParameterInDestructuringWithInitializer.js | 24 ++-------- ...eterInDestructuringWithInitializer.symbols | 44 +++++++------------ ...ameterInDestructuringWithInitializer.types | 26 ++--------- ...ParameterInDestructuringWithInitializer.ts | 14 ++---- 6 files changed, 32 insertions(+), 109 deletions(-) delete mode 100644 pull_request_template.md diff --git a/pull_request_template.md b/pull_request_template.md deleted file mode 100644 index 4243c2f671f..00000000000 --- a/pull_request_template.md +++ /dev/null @@ -1,17 +0,0 @@ - - -Fixes # -#17080 diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt index 8965d7172e8..b2fea589cb1 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt @@ -8,14 +8,14 @@ tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(31,8): e Type 'undefined' is not assignable to type 'number'. tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(45,10): error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'. Type 'undefined' is not assignable to type 'number'. -tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(55,11): error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number | undefined'. +tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(53,11): error TS2345: Argument of type 'number | null' is not assignable to parameter of type 'number | undefined'. Type 'null' is not assignable to type 'number | undefined'. ==== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts (6 errors) ==== // https://github.com/Microsoft/TypeScript/issues/17080 - function f(a:number,b:number) { - } + + declare function f(a:number,b:number): void; function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { f(a, b) @@ -75,9 +75,7 @@ tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(55,11): !!! error TS2345: Type 'undefined' is not assignable to type 'number'. } - function useBar(bar: number) { - f(bar, 1) - } + declare function useBar(bar: number): void; performFoo(); @@ -88,11 +86,7 @@ tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts(55,11): !!! error TS2345: Type 'null' is not assignable to type 'number | undefined'. } - function useBar2(bar: number | undefined) { - if (bar) { - f(bar, 1) - } - } + declare function useBar2(bar: number | undefined): void; performFoo2(); \ No newline at end of file diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js index cb929d89112..642f00a4f50 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js @@ -1,7 +1,7 @@ //// [optionalParameterInDestructuringWithInitializer.ts] // https://github.com/Microsoft/TypeScript/issues/17080 -function f(a:number,b:number) { -} + +declare function f(a:number,b:number): void; function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { f(a, b) @@ -46,9 +46,7 @@ function performFoo({ bar }: Foo = {}) { useBar(bar); } -function useBar(bar: number) { - f(bar, 1) -} +declare function useBar(bar: number): void; performFoo(); @@ -56,19 +54,13 @@ function performFoo2({ bar = null }: Foo = {}) { useBar2(bar); } -function useBar2(bar: number | undefined) { - if (bar) { - f(bar, 1) - } -} +declare function useBar2(bar: number | undefined): void; performFoo2(); //// [optionalParameterInDestructuringWithInitializer.js] // https://github.com/Microsoft/TypeScript/issues/17080 -function f(a, b) { -} function func1(_a) { var _b = _a === void 0 ? { a: 1, b: 2 } : _a, a = _b.a, b = _b.b; f(a, b); @@ -108,17 +100,9 @@ function performFoo(_a) { var bar = (_a === void 0 ? {} : _a).bar; useBar(bar); } -function useBar(bar) { - f(bar, 1); -} performFoo(); function performFoo2(_a) { var _b = (_a === void 0 ? {} : _a).bar, bar = _b === void 0 ? null : _b; useBar2(bar); } -function useBar2(bar) { - if (bar) { - f(bar, 1); - } -} performFoo2(); diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols index 399956476c8..6d4cf484d92 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols @@ -1,13 +1,13 @@ === tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === // https://github.com/Microsoft/TypeScript/issues/17080 -function f(a:number,b:number) { + +declare function f(a:number,b:number): void; >f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) ->a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 1, 11)) ->b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 1, 20)) -} +>a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 2, 19)) +>b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 2, 28)) function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { ->func1 : Symbol(func1, Decl(optionalParameterInDestructuringWithInitializer.ts, 2, 1)) +>func1 : Symbol(func1, Decl(optionalParameterInDestructuringWithInitializer.ts, 2, 44)) >a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 17)) >b : Symbol(b, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 19)) >a : Symbol(a, Decl(optionalParameterInDestructuringWithInitializer.ts, 4, 25)) @@ -169,41 +169,27 @@ function performFoo({ bar }: Foo = {}) { >bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 43, 21)) } -function useBar(bar: number) { +declare function useBar(bar: number): void; >useBar : Symbol(useBar, Decl(optionalParameterInDestructuringWithInitializer.ts, 45, 1)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 47, 16)) - - f(bar, 1) ->f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 47, 16)) -} +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 47, 24)) performFoo(); >performFoo : Symbol(performFoo, Decl(optionalParameterInDestructuringWithInitializer.ts, 41, 1)) function performFoo2({ bar = null }: Foo = {}) { ->performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 13)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 22)) +>performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 49, 13)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 22)) >Foo : Symbol(Foo, Decl(optionalParameterInDestructuringWithInitializer.ts, 37, 1)) useBar2(bar); ->useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 55, 1)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 22)) +>useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 22)) } -function useBar2(bar: number | undefined) { ->useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 55, 1)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) - - if (bar) { ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) - - f(bar, 1) ->f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) ->bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 57, 17)) - } -} +declare function useBar2(bar: number | undefined): void; +>useBar2 : Symbol(useBar2, Decl(optionalParameterInDestructuringWithInitializer.ts, 53, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 55, 25)) performFoo2(); ->performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 13)) +>performFoo2 : Symbol(performFoo2, Decl(optionalParameterInDestructuringWithInitializer.ts, 49, 13)) diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types index 1d1618e12e3..26110e71051 100644 --- a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types @@ -1,10 +1,10 @@ === tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === // https://github.com/Microsoft/TypeScript/issues/17080 -function f(a:number,b:number) { + +declare function f(a:number,b:number): void; >f : (a: number, b: number) => void >a : number >b : number -} function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { >func1 : ({ a, b }?: { a: number; b?: number | undefined; }) => void @@ -215,17 +215,10 @@ function performFoo({ bar }: Foo = {}) { >bar : number | undefined } -function useBar(bar: number) { +declare function useBar(bar: number): void; >useBar : (bar: number) => void >bar : number - f(bar, 1) ->f(bar, 1) : void ->f : (a: number, b: number) => void ->bar : number ->1 : 1 -} - performFoo(); >performFoo() : void >performFoo : ({ bar }?: Foo) => void @@ -243,21 +236,10 @@ function performFoo2({ bar = null }: Foo = {}) { >bar : number | null } -function useBar2(bar: number | undefined) { +declare function useBar2(bar: number | undefined): void; >useBar2 : (bar: number | undefined) => void >bar : number | undefined - if (bar) { ->bar : number | undefined - - f(bar, 1) ->f(bar, 1) : void ->f : (a: number, b: number) => void ->bar : number ->1 : 1 - } -} - performFoo2(); >performFoo2() : void >performFoo2 : ({ bar }?: Foo) => void diff --git a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts index 1f1847a19a7..d3ebd99efa1 100644 --- a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts +++ b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts @@ -1,7 +1,7 @@ // @strictNullChecks: true // https://github.com/Microsoft/TypeScript/issues/17080 -function f(a:number,b:number) { -} + +declare function f(a:number,b:number): void; function func1( {a, b}: {a: number, b?: number} = {a: 1, b: 2} ) { f(a, b) @@ -46,9 +46,7 @@ function performFoo({ bar }: Foo = {}) { useBar(bar); } -function useBar(bar: number) { - f(bar, 1) -} +declare function useBar(bar: number): void; performFoo(); @@ -56,10 +54,6 @@ function performFoo2({ bar = null }: Foo = {}) { useBar2(bar); } -function useBar2(bar: number | undefined) { - if (bar) { - f(bar, 1) - } -} +declare function useBar2(bar: number | undefined): void; performFoo2(); From 97fb0fd55f28d9c599f14ad8487b85367bdf7da2 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 22 Jan 2018 12:59:53 -0800 Subject: [PATCH 075/100] Add semicolon to unused class member list Turns out SemicolonClassElement is a specific kind for semicolons inside a class. Having one of them with --noUnusedLocals on would crash the compiler after the assert added in #21013. --- src/compiler/checker.ts | 1 + tests/baselines/reference/unusedSemicolonInClass.js | 13 +++++++++++++ .../reference/unusedSemicolonInClass.symbols | 7 +++++++ .../reference/unusedSemicolonInClass.types | 7 +++++++ tests/cases/compiler/unusedSemicolonInClass.ts | 4 ++++ 5 files changed, 32 insertions(+) create mode 100644 tests/baselines/reference/unusedSemicolonInClass.js create mode 100644 tests/baselines/reference/unusedSemicolonInClass.symbols create mode 100644 tests/baselines/reference/unusedSemicolonInClass.types create mode 100644 tests/cases/compiler/unusedSemicolonInClass.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 14631fa69cb..f418dbabcb8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -21030,6 +21030,7 @@ namespace ts { } break; case SyntaxKind.IndexSignature: + case SyntaxKind.SemicolonClassElement: // Can't be private break; default: diff --git a/tests/baselines/reference/unusedSemicolonInClass.js b/tests/baselines/reference/unusedSemicolonInClass.js new file mode 100644 index 00000000000..c33f799fec4 --- /dev/null +++ b/tests/baselines/reference/unusedSemicolonInClass.js @@ -0,0 +1,13 @@ +//// [unusedSemicolonInClass.ts] +class Unused { + ; +} + + +//// [unusedSemicolonInClass.js] +var Unused = /** @class */ (function () { + function Unused() { + } + ; + return Unused; +}()); diff --git a/tests/baselines/reference/unusedSemicolonInClass.symbols b/tests/baselines/reference/unusedSemicolonInClass.symbols new file mode 100644 index 00000000000..0e1832d1f3a --- /dev/null +++ b/tests/baselines/reference/unusedSemicolonInClass.symbols @@ -0,0 +1,7 @@ +=== tests/cases/compiler/unusedSemicolonInClass.ts === +class Unused { +>Unused : Symbol(Unused, Decl(unusedSemicolonInClass.ts, 0, 0)) + + ; +} + diff --git a/tests/baselines/reference/unusedSemicolonInClass.types b/tests/baselines/reference/unusedSemicolonInClass.types new file mode 100644 index 00000000000..118259662f4 --- /dev/null +++ b/tests/baselines/reference/unusedSemicolonInClass.types @@ -0,0 +1,7 @@ +=== tests/cases/compiler/unusedSemicolonInClass.ts === +class Unused { +>Unused : Unused + + ; +} + diff --git a/tests/cases/compiler/unusedSemicolonInClass.ts b/tests/cases/compiler/unusedSemicolonInClass.ts new file mode 100644 index 00000000000..5cd62690ba9 --- /dev/null +++ b/tests/cases/compiler/unusedSemicolonInClass.ts @@ -0,0 +1,4 @@ +// @noUnusedLocals: true +class Unused { + ; +} From 7b449a5e6230b5ec9445902a87148a698719baa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stephan=20Ginth=C3=B6r?= <26004708+Lazarus535@users.noreply.github.com> Date: Mon, 22 Jan 2018 22:09:35 +0100 Subject: [PATCH 076/100] Fixes #17080 Readded untouched pull_request_template.md --- pull_request_template.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pull_request_template.md diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000000..2c49c84641b --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,16 @@ + + +Fixes # From ae652404cdcf9821e7a397d7c16995ef2111baed Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Mon, 22 Jan 2018 13:34:12 -0800 Subject: [PATCH 077/100] Fix JSX attribute checking when spreading unions Previously, the code didn't account for the fact that spreading a union creates a union. In fact, before Decemeber, spreading a union in JSX didn't create a union. Now the check for properties of the spread type uses `getPropertiesOfType`, which works with unions, instead of accessing the `properties` property directly. --- src/compiler/checker.ts | 2 +- .../tsxSpreadAttributesResolution17.js | 47 +++++++++++++++++++ .../tsxSpreadAttributesResolution17.symbols | 41 ++++++++++++++++ .../tsxSpreadAttributesResolution17.types | 45 ++++++++++++++++++ .../jsx/tsxSpreadAttributesResolution17.tsx | 25 ++++++++++ 5 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/tsxSpreadAttributesResolution17.js create mode 100644 tests/baselines/reference/tsxSpreadAttributesResolution17.symbols create mode 100644 tests/baselines/reference/tsxSpreadAttributesResolution17.types create mode 100644 tests/cases/conformance/jsx/tsxSpreadAttributesResolution17.tsx diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f418dbabcb8..f4d8c99c471 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15310,7 +15310,7 @@ namespace ts { // If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type. // but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass. - if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || (sourceAttributesType).properties.length > 0)) { + if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || getPropertiesOfType(sourceAttributesType).length > 0)) { error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, unescapeLeadingUnderscores(getJsxElementPropertiesName())); } else { diff --git a/tests/baselines/reference/tsxSpreadAttributesResolution17.js b/tests/baselines/reference/tsxSpreadAttributesResolution17.js new file mode 100644 index 00000000000..81b1f073514 --- /dev/null +++ b/tests/baselines/reference/tsxSpreadAttributesResolution17.js @@ -0,0 +1,47 @@ +//// [file.tsx] +declare global { + namespace JSX { + interface Element {} + interface ElementAttributesProperty { props: {} } + } +} +declare var React: any; + +export class Empty extends React.Component<{}, {}> { + render() { + return
Hello
; + } +} + +declare const obj: { a: number | undefined } | undefined; + +// OK +let unionedSpread = ; + + +//// [file.jsx] +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +exports.__esModule = true; +var Empty = /** @class */ (function (_super) { + __extends(Empty, _super); + function Empty() { + return _super !== null && _super.apply(this, arguments) || this; + } + Empty.prototype.render = function () { + return
Hello
; + }; + return Empty; +}(React.Component)); +exports.Empty = Empty; +// OK +var unionedSpread = ; diff --git a/tests/baselines/reference/tsxSpreadAttributesResolution17.symbols b/tests/baselines/reference/tsxSpreadAttributesResolution17.symbols new file mode 100644 index 00000000000..6f346ccdcb1 --- /dev/null +++ b/tests/baselines/reference/tsxSpreadAttributesResolution17.symbols @@ -0,0 +1,41 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare global { +>global : Symbol(global, Decl(file.tsx, 0, 0)) + + namespace JSX { +>JSX : Symbol(JSX, Decl(file.tsx, 0, 16)) + + interface Element {} +>Element : Symbol(Element, Decl(file.tsx, 1, 19)) + + interface ElementAttributesProperty { props: {} } +>ElementAttributesProperty : Symbol(ElementAttributesProperty, Decl(file.tsx, 2, 28)) +>props : Symbol(ElementAttributesProperty.props, Decl(file.tsx, 3, 45)) + } +} +declare var React: any; +>React : Symbol(React, Decl(file.tsx, 6, 11)) + +export class Empty extends React.Component<{}, {}> { +>Empty : Symbol(Empty, Decl(file.tsx, 6, 23)) +>React : Symbol(React, Decl(file.tsx, 6, 11)) + + render() { +>render : Symbol(Empty.render, Decl(file.tsx, 8, 52)) + + return
Hello
; +>div : Symbol(unknown) +>div : Symbol(unknown) + } +} + +declare const obj: { a: number | undefined } | undefined; +>obj : Symbol(obj, Decl(file.tsx, 14, 13)) +>a : Symbol(a, Decl(file.tsx, 14, 20)) + +// OK +let unionedSpread = ; +>unionedSpread : Symbol(unionedSpread, Decl(file.tsx, 17, 3)) +>Empty : Symbol(Empty, Decl(file.tsx, 6, 23)) +>obj : Symbol(obj, Decl(file.tsx, 14, 13)) + diff --git a/tests/baselines/reference/tsxSpreadAttributesResolution17.types b/tests/baselines/reference/tsxSpreadAttributesResolution17.types new file mode 100644 index 00000000000..9477e7366ee --- /dev/null +++ b/tests/baselines/reference/tsxSpreadAttributesResolution17.types @@ -0,0 +1,45 @@ +=== tests/cases/conformance/jsx/file.tsx === +declare global { +>global : any + + namespace JSX { +>JSX : any + + interface Element {} +>Element : Element + + interface ElementAttributesProperty { props: {} } +>ElementAttributesProperty : ElementAttributesProperty +>props : {} + } +} +declare var React: any; +>React : any + +export class Empty extends React.Component<{}, {}> { +>Empty : Empty +>React.Component : any +>React : any +>Component : any + + render() { +>render : () => JSX.Element + + return
Hello
; +>
Hello
: JSX.Element +>div : any +>div : any + } +} + +declare const obj: { a: number | undefined } | undefined; +>obj : { a: number | undefined; } | undefined +>a : number | undefined + +// OK +let unionedSpread = ; +>unionedSpread : JSX.Element +> : JSX.Element +>Empty : typeof Empty +>obj : { a: number | undefined; } | undefined + diff --git a/tests/cases/conformance/jsx/tsxSpreadAttributesResolution17.tsx b/tests/cases/conformance/jsx/tsxSpreadAttributesResolution17.tsx new file mode 100644 index 00000000000..da5638d3e2a --- /dev/null +++ b/tests/cases/conformance/jsx/tsxSpreadAttributesResolution17.tsx @@ -0,0 +1,25 @@ +// @strictNullChecks: true +// @filename: file.tsx +// @jsx: preserve +// @noLib: true +// @skipLibCheck: true +// @libFiles: lib.d.ts + +declare global { + namespace JSX { + interface Element {} + interface ElementAttributesProperty { props: {} } + } +} +declare var React: any; + +export class Empty extends React.Component<{}, {}> { + render() { + return
Hello
; + } +} + +declare const obj: { a: number | undefined } | undefined; + +// OK +let unionedSpread = ; From a05f669f43d4e52b2fe4e349d9968b61d3090ae4 Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Mon, 22 Jan 2018 16:40:29 -0800 Subject: [PATCH 078/100] Expose sort and deduplicate diagnostics in Public API Fixes #20876 --- src/compiler/core.ts | 8 ++++---- tests/baselines/reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ae506fd616a..e927bd35382 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -16,6 +16,10 @@ namespace ts { // Update: We also consider a path like `C:\foo.ts` "relative" because we do not search for it in `node_modules` or treat it as an ambient module. return pathIsRelative(moduleName) || isRootedDiskPath(moduleName); } + + export function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray): Diagnostic[] { + return sortAndDeduplicate(diagnostics, compareDiagnostics); + } } /* @internal */ @@ -1897,10 +1901,6 @@ namespace ts { return text1 ? Comparison.GreaterThan : Comparison.LessThan; } - export function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray): Diagnostic[] { - return sortAndDeduplicate(diagnostics, compareDiagnostics); - } - export function normalizeSlashes(path: string): string { return path.replace(/\\/g, "/"); } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index cd1b577b711..8c244f3d4e0 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2814,6 +2814,7 @@ declare namespace ts { } declare namespace ts { function isExternalModuleNameRelative(moduleName: string): boolean; + function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray): Diagnostic[]; } declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any; declare function clearTimeout(handle: any): void; diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3cbca96f8fa..2933d11e5cb 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2814,6 +2814,7 @@ declare namespace ts { } declare namespace ts { function isExternalModuleNameRelative(moduleName: string): boolean; + function sortAndDeduplicateDiagnostics(diagnostics: ReadonlyArray): Diagnostic[]; } declare function setTimeout(handler: (...args: any[]) => void, timeout: number): any; declare function clearTimeout(handle: any): void; From 6cc17f1cd9169ec02eb1d5a0573f3691ab561465 Mon Sep 17 00:00:00 2001 From: csigs Date: Tue, 23 Jan 2018 10:10:51 +0000 Subject: [PATCH 079/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index b6b35ec094b..7ef4d27d021 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -582,6 +582,15 @@ + + + + + + + + + @@ -3378,15 +3387,6 @@ - - - - - - - - - @@ -3573,6 +3573,15 @@ + + + + + + + + + @@ -6603,11 +6612,11 @@ - + - + - + From 0e46086e7e7323461306bbb1ff4261e2800a283a Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 23 Jan 2018 08:09:15 -0800 Subject: [PATCH 080/100] In getSpecialPropertyExport, add debug failure when symbol parent is not a module (#21347) * In getSpecialPropertyExport, add debug failure when symbol parent is not a module * Fix lint --- src/services/importTracker.ts | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/services/importTracker.ts b/src/services/importTracker.ts index d2ce802f177..a8f724cb4be 100644 --- a/src/services/importTracker.ts +++ b/src/services/importTracker.ts @@ -510,11 +510,32 @@ namespace ts.FindAllReferences { return undefined; } - const sym = useLhsSymbol ? checker.getSymbolAtLocation((node.left as ts.PropertyAccessExpression).name) : symbol; + const sym = useLhsSymbol ? checker.getSymbolAtLocation(cast(node.left, isPropertyAccessExpression).name) : symbol; + // Better detection for GH#20803 + if (sym && !(checker.getMergedSymbol(sym.parent).flags & SymbolFlags.Module)) { + Debug.fail(`Special property assignment kind does not have a module as its parent. Assignment is ${showSymbol(sym)}, parent is ${showSymbol(sym.parent)}`); + } return sym && exportInfo(sym, kind); } } + function showSymbol(s: Symbol): string { + const decls = s.declarations.map(d => (ts as any).SyntaxKind[d.kind]).join(","); + const flags = showFlags(s.flags, (ts as any).SymbolFlags); + return `{ declarations: ${decls}, flags: ${flags} }`; + } + + function showFlags(f: number, flags: any) { + const out = []; + for (let pow = 0; pow <= 30; pow++) { + const n = 1 << pow; + if (f & n) { + out.push(flags[n]); + } + } + return out.join("|"); + } + function getImport(): ImportedSymbol | undefined { const isImport = isNodeImport(node); if (!isImport) return undefined; From 9dc01bf8bdd4a5c98c14fd1d21c302bc0df47173 Mon Sep 17 00:00:00 2001 From: csigs Date: Tue, 23 Jan 2018 17:10:12 +0000 Subject: [PATCH 081/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- 5 files changed, 105 insertions(+), 60 deletions(-) diff --git a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl index a678f69e80e..7ae7fab1b3b 100644 --- a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,11 @@ - + - + - + diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index a58de23e3ff..7cc0cf16633 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,11 @@ - + - + - + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index d111d8f84b5..1262a3743cf 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -582,6 +582,15 @@ + + + + + + + + + @@ -3378,15 +3387,6 @@ - - - - - - - - - @@ -3573,6 +3573,15 @@ + + + + + + + + + @@ -6603,11 +6612,11 @@ - + - + - + diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1f62aefc8ab..551a0e28806 100644 --- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,11 @@ - + - + - + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index 2481a1b8752..d9b0532182c 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,11 @@ - + - + - + From d4b3bd16c4deaec427fbfdc304cdd1b23a36de2a Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 23 Jan 2018 10:57:35 -0800 Subject: [PATCH 082/100] Add KeywordCompletionFilters.TypeKeywords (#21364) --- src/services/completions.ts | 86 ++++++++----------- src/services/utilities.ts | 4 + .../fourslash/completionsTypeKeywords.ts | 7 ++ 3 files changed, 46 insertions(+), 51 deletions(-) create mode 100644 tests/cases/fourslash/completionsTypeKeywords.ts diff --git a/src/services/completions.ts b/src/services/completions.ts index 80fdf6c280e..8b7f564ff08 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -20,7 +20,8 @@ namespace ts.Completions { None, ClassElementKeywords, // Keywords at class keyword ConstructorParameterKeywords, // Keywords at constructor parameter - FunctionLikeBodyKeywords // Keywords at function like body + FunctionLikeBodyKeywords, // Keywords at function like body + TypeKeywords, } export function getCompletionsAtPosition( @@ -565,7 +566,7 @@ namespace ts.Completions { } case "none": { // Didn't find a symbol with this name. See if we can find a keyword instead. - if (some(getKeywordCompletions(KeywordCompletionFilters.None), c => c.name === name)) { + if (allKeywordsCompletions().some(c => c.name === name)) { return { name, kind: ScriptElementKind.keyword, @@ -1163,6 +1164,9 @@ namespace ts.Completions { } function filterGlobalCompletion(symbols: Symbol[]): void { + const isTypeCompletion = insideJsDocTagTypeExpression || !isContextTokenValueLocation(contextToken) && (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)); + if (isTypeCompletion) keywordFilters = KeywordCompletionFilters.TypeKeywords; + filterMutate(symbols, symbol => { if (!isSourceFile(location)) { // export = /**/ here we want to get all meanings, so any symbol is ok @@ -1170,19 +1174,14 @@ namespace ts.Completions { return true; } - // This is an alias, follow what it aliases - if (symbol && symbol.flags & SymbolFlags.Alias) { - symbol = typeChecker.getAliasedSymbol(symbol); - } + symbol = skipAlias(symbol, typeChecker); // import m = /**/ <-- It can only access namespace (if typing import = x. this would get member symbols and not namespace) if (isInRightSideOfInternalImportEqualsDeclaration(location)) { return !!(symbol.flags & SymbolFlags.Namespace); } - if (insideJsDocTagTypeExpression || - (!isContextTokenValueLocation(contextToken) && - (isPartOfTypeNode(location) || isContextTokenTypeLocation(contextToken)))) { + if (isTypeCompletion) { // Its a type, but you can reach it by namespace.type as well return symbolCanBeReferencedAtTypeLocation(symbol); } @@ -1199,7 +1198,7 @@ namespace ts.Completions { contextToken.parent.kind === SyntaxKind.TypeQuery; } - function isContextTokenTypeLocation(contextToken: Node) { + function isContextTokenTypeLocation(contextToken: Node): boolean { if (contextToken) { const parentKind = contextToken.parent.kind; switch (contextToken.kind) { @@ -1217,6 +1216,7 @@ namespace ts.Completions { return parentKind === SyntaxKind.AsExpression; } } + return false; } function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean { @@ -2130,51 +2130,38 @@ namespace ts.Completions { } // A cache of completion entries for keywords, these do not change between sessions - const _keywordCompletions: CompletionEntry[][] = []; - function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): CompletionEntry[] { - const completions = _keywordCompletions[keywordFilter]; - if (completions) { - return completions; + const _keywordCompletions: ReadonlyArray[] = []; + const allKeywordsCompletions: () => ReadonlyArray = ts.memoize(() => { + const res: CompletionEntry[] = []; + for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) { + res.push({ + name: tokenToString(i), + kind: ScriptElementKind.keyword, + kindModifiers: ScriptElementKindModifier.none, + sortText: "0" + }); } - return _keywordCompletions[keywordFilter] = generateKeywordCompletions(keywordFilter); - - type FilterKeywordCompletions = (entryName: string) => boolean; - function generateKeywordCompletions(keywordFilter: KeywordCompletionFilters): CompletionEntry[] { + return res; + }); + function getKeywordCompletions(keywordFilter: KeywordCompletionFilters): ReadonlyArray { + return _keywordCompletions[keywordFilter] || (_keywordCompletions[keywordFilter] = allKeywordsCompletions().filter(entry => { + const kind = stringToToken(entry.name); switch (keywordFilter) { case KeywordCompletionFilters.None: - return getAllKeywordCompletions(); + // "undefined" is a global variable, so don't need a keyword completion for it. + return kind !== SyntaxKind.UndefinedKeyword; case KeywordCompletionFilters.ClassElementKeywords: - return getFilteredKeywordCompletions(isClassMemberCompletionKeywordText); + return isClassMemberCompletionKeyword(kind); case KeywordCompletionFilters.ConstructorParameterKeywords: - return getFilteredKeywordCompletions(isConstructorParameterCompletionKeywordText); + return isConstructorParameterCompletionKeyword(kind); case KeywordCompletionFilters.FunctionLikeBodyKeywords: - return getFilteredKeywordCompletions(isFunctionLikeBodyCompletionKeywordText); + return isFunctionLikeBodyCompletionKeyword(kind); + case KeywordCompletionFilters.TypeKeywords: + return isTypeKeyword(kind); default: - Debug.assertNever(keywordFilter); + return Debug.assertNever(keywordFilter); } - } - - function getAllKeywordCompletions() { - const allKeywordsCompletions: CompletionEntry[] = []; - for (let i = SyntaxKind.FirstKeyword; i <= SyntaxKind.LastKeyword; i++) { - // "undefined" is a global variable, so don't need a keyword completion for it. - if (i === SyntaxKind.UndefinedKeyword) continue; - allKeywordsCompletions.push({ - name: tokenToString(i), - kind: ScriptElementKind.keyword, - kindModifiers: ScriptElementKindModifier.none, - sortText: "0" - }); - } - return allKeywordsCompletions; - } - - function getFilteredKeywordCompletions(filterFn: FilterKeywordCompletions) { - return filter( - getKeywordCompletions(KeywordCompletionFilters.None), - entry => filterFn(entry.name) - ); - } + })); } function isClassMemberCompletionKeyword(kind: SyntaxKind) { @@ -2222,15 +2209,12 @@ namespace ts.Completions { case SyntaxKind.AbstractKeyword: case SyntaxKind.GetKeyword: case SyntaxKind.SetKeyword: + case SyntaxKind.UndefinedKeyword: return false; } return true; } - function isFunctionLikeBodyCompletionKeywordText(text: string) { - return isFunctionLikeBodyCompletionKeyword(stringToToken(text)); - } - function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { switch (kind) { case ts.SyntaxKind.EqualsEqualsEqualsToken: diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 9bb35359523..c724fa5643d 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1070,12 +1070,16 @@ namespace ts { export const typeKeywords: ReadonlyArray = [ SyntaxKind.AnyKeyword, SyntaxKind.BooleanKeyword, + SyntaxKind.KeyOfKeyword, SyntaxKind.NeverKeyword, + SyntaxKind.NullKeyword, SyntaxKind.NumberKeyword, SyntaxKind.ObjectKeyword, SyntaxKind.StringKeyword, SyntaxKind.SymbolKeyword, SyntaxKind.VoidKeyword, + SyntaxKind.UndefinedKeyword, + SyntaxKind.UniqueKeyword, ]; export function isTypeKeyword(kind: SyntaxKind): boolean { diff --git a/tests/cases/fourslash/completionsTypeKeywords.ts b/tests/cases/fourslash/completionsTypeKeywords.ts new file mode 100644 index 00000000000..198d6dddab4 --- /dev/null +++ b/tests/cases/fourslash/completionsTypeKeywords.ts @@ -0,0 +1,7 @@ +/// + +////type T = /**/ + +goTo.marker(); +verify.completionListContains("undefined", "undefined", undefined, "keyword"); +verify.not.completionListContains("await"); From 4f11dd68abf969b6ad0f7f08185e68e291cd54d5 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 23 Jan 2018 10:39:26 -0800 Subject: [PATCH 083/100] Handle extracting case clause expression as constant --- src/harness/unittests/extractConstants.ts | 7 +++++++ src/services/refactors/extractSymbol.ts | 7 +++++++ .../extractConstant_CaseClauseExpression.js | 13 +++++++++++++ .../extractConstant_CaseClauseExpression.ts | 13 +++++++++++++ 4 files changed, 40 insertions(+) create mode 100644 tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.js create mode 100644 tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.ts diff --git a/src/harness/unittests/extractConstants.ts b/src/harness/unittests/extractConstants.ts index 71b550cba8f..9dfb1264a70 100644 --- a/src/harness/unittests/extractConstants.ts +++ b/src/harness/unittests/extractConstants.ts @@ -273,6 +273,13 @@ let i: I = [#|{ a: 1 }|]; const myObj: { member(x: number, y: string): void } = { member: [#|(x, y) => x + y|], } +`); + + testExtractConstant("extractConstant_CaseClauseExpression", ` +switch (1) { + case [#|1|]: + break; +} `); }); diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index 479c387fc65..f9a46f04914 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -1321,6 +1321,13 @@ namespace ts.refactor.extractSymbol { } prevStatement = statement; } + + if (!prevStatement && isCaseClause(curr)) { + // We must have been in the expression of the case clause. + Debug.assert(isSwitchStatement(curr.parent.parent)); + return curr.parent.parent; + } + // There must be at least one statement since we started in one. Debug.assert(prevStatement !== undefined); return prevStatement; diff --git a/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.js b/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.js new file mode 100644 index 00000000000..0031ecdf0f9 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.js @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +switch (1) { + case /*[#|*/1/*|]*/: + break; +} + +// ==SCOPE::Extract to constant in enclosing scope== +const newLocal = 1; +switch (1) { + case /*RENAME*/newLocal: + break; +} diff --git a/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.ts b/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.ts new file mode 100644 index 00000000000..0031ecdf0f9 --- /dev/null +++ b/tests/baselines/reference/extractConstant/extractConstant_CaseClauseExpression.ts @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +switch (1) { + case /*[#|*/1/*|]*/: + break; +} + +// ==SCOPE::Extract to constant in enclosing scope== +const newLocal = 1; +switch (1) { + case /*RENAME*/newLocal: + break; +} From 2f3b06a3cd5ca8c394828fee9c0c96f85f54f903 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Tue, 23 Jan 2018 11:07:59 -0800 Subject: [PATCH 084/100] Handle extraction ranges including case clause expressions (mostly by rejecting them) Fixes #20559 --- src/harness/unittests/extractRanges.ts | 46 +++++++++++++++++++++++++ src/services/refactors/extractSymbol.ts | 11 ++++++ 2 files changed, 57 insertions(+) diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index 493c9639c3d..2810f323abf 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -365,6 +365,52 @@ switch (x) { refactor.extractSymbol.Messages.cannotExtractRange.message ]); + testExtractRangeFailed("extractRangeFailed14", + ` + switch(1) { + case [#|1: + break;|] + } + `, + [ + refactor.extractSymbol.Messages.cannotExtractRange.message + ]); + + testExtractRangeFailed("extractRangeFailed15", + ` + switch(1) { + case [#|1: + break|]; + } + `, + [ + refactor.extractSymbol.Messages.cannotExtractRange.message + ]); + + // Documentation only - it would be nice if the result were [$|1|] + testExtractRangeFailed("extractRangeFailed16", + ` + switch(1) { + [#|case 1|]: + break; + } + `, + [ + refactor.extractSymbol.Messages.cannotExtractRange.message + ]); + + // Documentation only - it would be nice if the result were [$|1|] + testExtractRangeFailed("extractRangeFailed17", + ` + switch(1) { + [#|case 1:|] + break; + } + `, + [ + refactor.extractSymbol.Messages.cannotExtractRange.message + ]); + testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.cannotExtractIdentifier.message]); }); } \ No newline at end of file diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index f9a46f04914..e8a2186aa8b 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -235,6 +235,17 @@ namespace ts.refactor.extractSymbol { break; } } + + if (!statements.length) { + // https://github.com/Microsoft/TypeScript/issues/20559 + // Ranges like [|case 1: break;|] will fail to populate `statements` because + // they will never find `start` in `start.parent.statements`. + // Consider: We could support ranges like [|case 1:|] by refining them to just + // the expression. + Debug.assert(isCaseClause(start.parent) && span.start < start.parent.expression.end); + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; + } + return { targetRange: { range: statements, facts: rangeFacts, declarations } }; } From cd833890778c01260accd0198ea8e7240e00e4df Mon Sep 17 00:00:00 2001 From: csigs Date: Tue, 23 Jan 2018 23:11:17 +0000 Subject: [PATCH 085/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- 4 files changed, 84 insertions(+), 48 deletions(-) diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index 63f0270005a..7579d5ce794 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,11 @@ - + - + - + diff --git a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 74cf056e95a..400187f844a 100644 --- a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -566,6 +566,15 @@ + + + + + + + + + @@ -3359,15 +3368,6 @@ - - - - - - - - - @@ -3554,6 +3554,15 @@ + + + + + + + + + @@ -6578,11 +6587,11 @@ - + - + - + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index ced875a6581..46dce16bc93 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -572,6 +572,15 @@ + + + + + + + + + @@ -3368,15 +3377,6 @@ - - - - - - - - - @@ -3563,6 +3563,15 @@ + + + + + + + + + @@ -6593,11 +6602,11 @@ - + - + - + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 3b9a3824b9f..c1894d0db09 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -566,6 +566,15 @@ + + + + + + + + + @@ -3362,15 +3371,6 @@ - - - - - - - - - @@ -3557,6 +3557,15 @@ + + + + + + + + + @@ -6587,11 +6596,11 @@ - + - + - + From d7ed6402a53c1aa432119273e6bb69a5a896c512 Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 24 Jan 2018 05:10:14 +0000 Subject: [PATCH 086/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 5d1bc276ee8..3ad2bbf63b2 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -582,6 +582,15 @@ + + + + + + + + + @@ -3378,15 +3387,6 @@ - - - - - - - - - @@ -3573,6 +3573,15 @@ + + + + + + + + + @@ -6603,11 +6612,11 @@ - + - + - + diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl index 4be92ea0534..b6290bed3fb 100644 --- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -566,6 +566,15 @@ + + + + + + + + + @@ -3359,15 +3368,6 @@ - - - - - - - - - @@ -3554,6 +3554,15 @@ + + + + + + + + + @@ -6578,11 +6587,11 @@ - + - + - + From 5df27c1cd69b54dc3218d0c9e681940966bebc70 Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 24 Jan 2018 11:10:38 +0000 Subject: [PATCH 087/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages.generated.json.lcl | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index afee7120010..7fe309d50f8 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3366,15 +3375,6 @@ - - - - - - - - - @@ -3561,6 +3561,15 @@ + + + + + + + + + @@ -6585,11 +6594,11 @@ - + - + - + From 77c5529e93ae1d5e4d21bc2bc3e8d573c7d69fa1 Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 24 Jan 2018 10:58:41 -0800 Subject: [PATCH 088/100] Make error span for wrong type arguments be just `<...>`,d not `f<...>(...)` (#21390) --- src/compiler/checker.ts | 4 +- src/compiler/utilities.ts | 5 ++ ...signingFromObjectToAnythingElse.errors.txt | 8 +-- ...hIncorrectNumberOfTypeArguments.errors.txt | 56 +++++++++---------- ...enericFunctionWithTypeArguments.errors.txt | 28 +++++----- ...lWithWrongNumberOfTypeArguments.errors.txt | 8 +-- ...torInvocationWithTooFewTypeArgs.errors.txt | 4 +- .../emptyTypeArgumentList.errors.txt | 8 +-- .../emptyTypeArgumentListWithNew.errors.txt | 8 +-- ...kedInsideItsContainingFunction1.errors.txt | 24 ++++---- .../genericDefaultsErrors.errors.txt | 8 +-- .../genericWithOpenTypeParameters1.errors.txt | 8 +-- ...sWithWrongNumberOfTypeArguments.errors.txt | 8 +-- ...NonGenericTypeWithTypeArguments.errors.txt | 12 ++-- ...citTypeParameterAndArgumentType.errors.txt | 4 +- .../reference/overloadResolution.errors.txt | 4 +- ...loadResolutionClassConstructors.errors.txt | 12 ++-- .../overloadResolutionConstructors.errors.txt | 4 +- ...loadsAndTypeArgumentArityErrors.errors.txt | 8 +-- .../parserConstructorAmbiguity3.errors.txt | 6 +- ...CallExpressionWithTypeArguments.errors.txt | 4 +- .../tooManyTypeParameters1.errors.txt | 12 ++-- ...OnFunctionsWithNoTypeParameters.errors.txt | 8 +-- .../reference/typeAssertions.errors.txt | 4 +- ...unctionCallsWithTypeParameters1.errors.txt | 16 +++--- 25 files changed, 138 insertions(+), 133 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01b3a047d1d..bb1fbbdd526 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16717,7 +16717,7 @@ namespace ts { const isDecorator = node.kind === SyntaxKind.Decorator; const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node); - let typeArguments: ReadonlyArray; + let typeArguments: NodeArray; if (!isTaggedTemplate && !isDecorator && !isJsxOpeningOrSelfClosingElement) { typeArguments = (node).typeArguments; @@ -16846,7 +16846,7 @@ namespace ts { max = Math.max(max, length(sig.typeParameters)); } const paramCount = min < max ? min + "-" + max : min; - diagnostics.add(createDiagnosticForNode(node, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length)); + diagnostics.add(createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, paramCount, typeArguments.length)); } else if (args) { let min = Number.POSITIVE_INFINITY; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index bf6a086967c..f4ddf1d8319 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -599,6 +599,11 @@ namespace ts { return createDiagnosticForNodeInSourceFile(sourceFile, node, message, arg0, arg1, arg2, arg3); } + export function createDiagnosticForNodeArray(sourceFile: SourceFile, nodes: NodeArray, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { + const start = skipTrivia(sourceFile.text, nodes.pos); + return createFileDiagnostic(sourceFile, start, nodes.end - start, message, arg0, arg1, arg2, arg3); + } + export function createDiagnosticForNodeInSourceFile(sourceFile: SourceFile, node: Node, message: DiagnosticMessage, arg0?: string | number, arg1?: string | number, arg2?: string | number, arg3?: string | number): Diagnostic { const span = getErrorSpanForNode(sourceFile, node); return createFileDiagnostic(sourceFile, span.start, span.length, message, arg0, arg1, arg2, arg3); diff --git a/tests/baselines/reference/assigningFromObjectToAnythingElse.errors.txt b/tests/baselines/reference/assigningFromObjectToAnythingElse.errors.txt index c8fb57e3616..bc92c376c7c 100644 --- a/tests/baselines/reference/assigningFromObjectToAnythingElse.errors.txt +++ b/tests/baselines/reference/assigningFromObjectToAnythingElse.errors.txt @@ -1,8 +1,8 @@ tests/cases/compiler/assigningFromObjectToAnythingElse.ts(3,1): error TS2322: Type 'Object' is not assignable to type 'RegExp'. The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead? Property 'exec' is missing in type 'Object'. -tests/cases/compiler/assigningFromObjectToAnythingElse.ts(5,17): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/assigningFromObjectToAnythingElse.ts(6,17): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/assigningFromObjectToAnythingElse.ts(5,31): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/assigningFromObjectToAnythingElse.ts(6,31): error TS2558: Expected 0 type arguments, but got 1. tests/cases/compiler/assigningFromObjectToAnythingElse.ts(8,5): error TS2322: Type 'Object' is not assignable to type 'Error'. The 'Object' type is assignable to very few other types. Did you mean to use the 'any' type instead? Property 'name' is missing in type 'Object'. @@ -18,10 +18,10 @@ tests/cases/compiler/assigningFromObjectToAnythingElse.ts(8,5): error TS2322: Ty !!! error TS2322: Property 'exec' is missing in type 'Object'. var a: String = Object.create(""); - ~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var c: String = Object.create(1); - ~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var w: Error = new Object(); diff --git a/tests/baselines/reference/callGenericFunctionWithIncorrectNumberOfTypeArguments.errors.txt b/tests/baselines/reference/callGenericFunctionWithIncorrectNumberOfTypeArguments.errors.txt index 6d95b588ebf..d5809d677e4 100644 --- a/tests/baselines/reference/callGenericFunctionWithIncorrectNumberOfTypeArguments.errors.txt +++ b/tests/baselines/reference/callGenericFunctionWithIncorrectNumberOfTypeArguments.errors.txt @@ -1,17 +1,17 @@ -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(5,10): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(6,11): error TS2558: Expected 2 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(9,10): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(10,11): error TS2558: Expected 2 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(13,10): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(14,11): error TS2558: Expected 2 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(21,10): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(22,11): error TS2558: Expected 2 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(28,10): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(29,11): error TS2558: Expected 2 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(36,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(37,11): error TS2558: Expected 0 type arguments, but got 3. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(43,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(44,11): error TS2558: Expected 0 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(5,12): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(6,13): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(9,13): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(10,14): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(13,13): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(14,14): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(21,22): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(22,23): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(28,14): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(29,15): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(36,23): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(37,24): error TS2558: Expected 0 type arguments, but got 3. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(43,15): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts(44,16): error TS2558: Expected 0 type arguments, but got 3. ==== tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFunctionWithIncorrectNumberOfTypeArguments.ts (14 errors) ==== @@ -20,26 +20,26 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFuncti function f(x: T, y: U): T { return null; } var r1 = f(1, ''); - ~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r1b = f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. var f2 = (x: T, y: U): T => { return null; } var r2 = f2(1, ''); - ~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r2b = f2(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. var f3: { (x: T, y: U): T; } var r3 = f3(1, ''); - ~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r3b = f3(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. class C { @@ -48,10 +48,10 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFuncti } } var r4 = (new C()).f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r4b = (new C()).f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. interface I { @@ -59,10 +59,10 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFuncti } var i: I; var r5 = i.f(1, ''); - ~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r5b = i.f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. class C2 { @@ -71,10 +71,10 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFuncti } } var r6 = (new C2()).f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r6b = (new C2()).f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 3. interface I2 { @@ -82,8 +82,8 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callGenericFuncti } var i2: I2; var r7 = i2.f(1, ''); - ~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r7b = i2.f(1, ''); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 3. \ No newline at end of file diff --git a/tests/baselines/reference/callNonGenericFunctionWithTypeArguments.errors.txt b/tests/baselines/reference/callNonGenericFunctionWithTypeArguments.errors.txt index b014eae0fd8..6bfb0c1113e 100644 --- a/tests/baselines/reference/callNonGenericFunctionWithTypeArguments.errors.txt +++ b/tests/baselines/reference/callNonGenericFunctionWithTypeArguments.errors.txt @@ -1,10 +1,10 @@ -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(5,9): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(8,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(11,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(18,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(24,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(31,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(37,10): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(5,11): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(8,13): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(11,13): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(18,22): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(24,14): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(31,23): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(37,15): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(40,10): error TS2347: Untyped function calls may not accept type arguments. tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFunctionWithTypeArguments.ts(43,10): error TS2347: Untyped function calls may not accept type arguments. @@ -15,17 +15,17 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFun function f(x: number) { return null; } var r = f(1); - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var f2 = (x: number) => { return null; } var r2 = f2(1); - ~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var f3: { (x: number): any; } var r3 = f3(1); - ~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. class C { @@ -34,7 +34,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFun } } var r4 = (new C()).f(1); - ~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. interface I { @@ -42,7 +42,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFun } var i: I; var r5 = i.f(1); - ~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. class C2 { @@ -51,7 +51,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFun } } var r6 = (new C2()).f(1); - ~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. interface I2 { @@ -59,7 +59,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/callNonGenericFun } var i2: I2; var r7 = i2.f(1); - ~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var a; diff --git a/tests/baselines/reference/callWithWrongNumberOfTypeArguments.errors.txt b/tests/baselines/reference/callWithWrongNumberOfTypeArguments.errors.txt index e3fca9dff9c..c8553e8fc0d 100644 --- a/tests/baselines/reference/callWithWrongNumberOfTypeArguments.errors.txt +++ b/tests/baselines/reference/callWithWrongNumberOfTypeArguments.errors.txt @@ -1,14 +1,14 @@ -tests/cases/compiler/callWithWrongNumberOfTypeArguments.ts(3,1): error TS2558: Expected 2 type arguments, but got 1. -tests/cases/compiler/callWithWrongNumberOfTypeArguments.ts(5,1): error TS2558: Expected 2 type arguments, but got 3. +tests/cases/compiler/callWithWrongNumberOfTypeArguments.ts(3,3): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/compiler/callWithWrongNumberOfTypeArguments.ts(5,3): error TS2558: Expected 2 type arguments, but got 3. ==== tests/cases/compiler/callWithWrongNumberOfTypeArguments.ts (2 errors) ==== function f() { } f(); - ~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. f(); f(); - ~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 3. \ No newline at end of file diff --git a/tests/baselines/reference/constructorInvocationWithTooFewTypeArgs.errors.txt b/tests/baselines/reference/constructorInvocationWithTooFewTypeArgs.errors.txt index 265ce4686db..10dd4ad82fb 100644 --- a/tests/baselines/reference/constructorInvocationWithTooFewTypeArgs.errors.txt +++ b/tests/baselines/reference/constructorInvocationWithTooFewTypeArgs.errors.txt @@ -1,4 +1,4 @@ -tests/cases/compiler/constructorInvocationWithTooFewTypeArgs.ts(9,9): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/compiler/constructorInvocationWithTooFewTypeArgs.ts(9,15): error TS2558: Expected 2 type arguments, but got 1. ==== tests/cases/compiler/constructorInvocationWithTooFewTypeArgs.ts (1 errors) ==== @@ -11,6 +11,6 @@ tests/cases/compiler/constructorInvocationWithTooFewTypeArgs.ts(9,9): error TS25 } var d = new D(); - ~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. \ No newline at end of file diff --git a/tests/baselines/reference/emptyTypeArgumentList.errors.txt b/tests/baselines/reference/emptyTypeArgumentList.errors.txt index 036f307447b..395c990a3d9 100644 --- a/tests/baselines/reference/emptyTypeArgumentList.errors.txt +++ b/tests/baselines/reference/emptyTypeArgumentList.errors.txt @@ -1,11 +1,11 @@ -tests/cases/compiler/emptyTypeArgumentList.ts(2,1): error TS2558: Expected 1 type arguments, but got 0. tests/cases/compiler/emptyTypeArgumentList.ts(2,4): error TS1099: Type argument list cannot be empty. +tests/cases/compiler/emptyTypeArgumentList.ts(2,5): error TS2558: Expected 1 type arguments, but got 0. ==== tests/cases/compiler/emptyTypeArgumentList.ts (2 errors) ==== function foo() { } foo<>(); - ~~~~~~~ -!!! error TS2558: Expected 1 type arguments, but got 0. ~~ -!!! error TS1099: Type argument list cannot be empty. \ No newline at end of file +!!! error TS1099: Type argument list cannot be empty. + +!!! error TS2558: Expected 1 type arguments, but got 0. \ No newline at end of file diff --git a/tests/baselines/reference/emptyTypeArgumentListWithNew.errors.txt b/tests/baselines/reference/emptyTypeArgumentListWithNew.errors.txt index 648aa7c6c50..92eb250291a 100644 --- a/tests/baselines/reference/emptyTypeArgumentListWithNew.errors.txt +++ b/tests/baselines/reference/emptyTypeArgumentListWithNew.errors.txt @@ -1,11 +1,11 @@ -tests/cases/compiler/emptyTypeArgumentListWithNew.ts(2,1): error TS2558: Expected 1 type arguments, but got 0. tests/cases/compiler/emptyTypeArgumentListWithNew.ts(2,8): error TS1099: Type argument list cannot be empty. +tests/cases/compiler/emptyTypeArgumentListWithNew.ts(2,9): error TS2558: Expected 1 type arguments, but got 0. ==== tests/cases/compiler/emptyTypeArgumentListWithNew.ts (2 errors) ==== class foo { } new foo<>(); - ~~~~~~~~~~~ -!!! error TS2558: Expected 1 type arguments, but got 0. ~~ -!!! error TS1099: Type argument list cannot be empty. \ No newline at end of file +!!! error TS1099: Type argument list cannot be empty. + +!!! error TS2558: Expected 1 type arguments, but got 0. \ No newline at end of file diff --git a/tests/baselines/reference/genericCallbackInvokedInsideItsContainingFunction1.errors.txt b/tests/baselines/reference/genericCallbackInvokedInsideItsContainingFunction1.errors.txt index 7ff051bd63e..ff4c2235b49 100644 --- a/tests/baselines/reference/genericCallbackInvokedInsideItsContainingFunction1.errors.txt +++ b/tests/baselines/reference/genericCallbackInvokedInsideItsContainingFunction1.errors.txt @@ -1,32 +1,32 @@ -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(2,14): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(2,16): error TS2558: Expected 0 type arguments, but got 1. tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(3,16): error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(4,14): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(8,15): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(9,15): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(4,16): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(8,17): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(9,17): error TS2558: Expected 0 type arguments, but got 1. tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(12,17): error TS2345: Argument of type 'U' is not assignable to parameter of type 'T'. -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(13,15): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(14,15): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(13,17): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(14,17): error TS2558: Expected 0 type arguments, but got 1. ==== tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts (8 errors) ==== function foo(x:T, y:U, f: (v: T) => U) { var r1 = f(1); - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r2 = f(1); ~ !!! error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. var r3 = f(null); - ~~~~~~~~~~~~ + ~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r4 = f(null); var r11 = f(x); var r21 = f(x); - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r31 = f(null); - ~~~~~~~~~~~~ + ~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r41 = f(null); @@ -34,10 +34,10 @@ tests/cases/compiler/genericCallbackInvokedInsideItsContainingFunction1.ts(14,15 ~ !!! error TS2345: Argument of type 'U' is not assignable to parameter of type 'T'. var r22 = f(y); - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r32 = f(null); - ~~~~~~~~~~~~ + ~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r42 = f(null); } \ No newline at end of file diff --git a/tests/baselines/reference/genericDefaultsErrors.errors.txt b/tests/baselines/reference/genericDefaultsErrors.errors.txt index b1afd6f173b..e1dca77d958 100644 --- a/tests/baselines/reference/genericDefaultsErrors.errors.txt +++ b/tests/baselines/reference/genericDefaultsErrors.errors.txt @@ -3,8 +3,8 @@ tests/cases/compiler/genericDefaultsErrors.ts(4,59): error TS2344: Type 'T' does Type 'string' is not assignable to type 'number'. tests/cases/compiler/genericDefaultsErrors.ts(5,44): error TS2344: Type 'T' does not satisfy the constraint 'number'. tests/cases/compiler/genericDefaultsErrors.ts(6,39): error TS2344: Type 'number' does not satisfy the constraint 'T'. -tests/cases/compiler/genericDefaultsErrors.ts(10,1): error TS2558: Expected 2-3 type arguments, but got 1. -tests/cases/compiler/genericDefaultsErrors.ts(13,1): error TS2558: Expected 2-3 type arguments, but got 4. +tests/cases/compiler/genericDefaultsErrors.ts(10,5): error TS2558: Expected 2-3 type arguments, but got 1. +tests/cases/compiler/genericDefaultsErrors.ts(13,5): error TS2558: Expected 2-3 type arguments, but got 4. tests/cases/compiler/genericDefaultsErrors.ts(17,13): error TS2345: Argument of type '"a"' is not assignable to parameter of type 'number'. tests/cases/compiler/genericDefaultsErrors.ts(19,11): error TS2428: All declarations of 'i00' must have identical type parameters. tests/cases/compiler/genericDefaultsErrors.ts(20,11): error TS2428: All declarations of 'i00' must have identical type parameters. @@ -44,12 +44,12 @@ tests/cases/compiler/genericDefaultsErrors.ts(42,29): error TS2716: Type paramet declare function f11(): void; f11(); // ok f11<1>(); // error - ~~~~~~~~ + ~ !!! error TS2558: Expected 2-3 type arguments, but got 1. f11<1, 2>(); // ok f11<1, 2, 3>(); // ok f11<1, 2, 3, 4>(); // error - ~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~ !!! error TS2558: Expected 2-3 type arguments, but got 4. declare function f12(a?: U): void; diff --git a/tests/baselines/reference/genericWithOpenTypeParameters1.errors.txt b/tests/baselines/reference/genericWithOpenTypeParameters1.errors.txt index 9f8694d2fca..6a243650b1e 100644 --- a/tests/baselines/reference/genericWithOpenTypeParameters1.errors.txt +++ b/tests/baselines/reference/genericWithOpenTypeParameters1.errors.txt @@ -1,6 +1,6 @@ tests/cases/compiler/genericWithOpenTypeParameters1.ts(7,40): error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. -tests/cases/compiler/genericWithOpenTypeParameters1.ts(8,35): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/genericWithOpenTypeParameters1.ts(9,35): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericWithOpenTypeParameters1.ts(8,41): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/genericWithOpenTypeParameters1.ts(9,41): error TS2558: Expected 0 type arguments, but got 1. ==== tests/cases/compiler/genericWithOpenTypeParameters1.ts (3 errors) ==== @@ -14,10 +14,10 @@ tests/cases/compiler/genericWithOpenTypeParameters1.ts(9,35): error TS2558: Expe ~ !!! error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. var f2 = (x: B) => { return x.foo(1); } // error - ~~~~~~~~~~~ + ~ !!! error TS2558: Expected 0 type arguments, but got 1. var f3 = (x: B) => { return x.foo(1); } // error - ~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var f4 = (x: B) => { return x.foo(1); } // no error \ No newline at end of file diff --git a/tests/baselines/reference/instantiateGenericClassWithWrongNumberOfTypeArguments.errors.txt b/tests/baselines/reference/instantiateGenericClassWithWrongNumberOfTypeArguments.errors.txt index 05cc1ee1cd0..a09b424b8e1 100644 --- a/tests/baselines/reference/instantiateGenericClassWithWrongNumberOfTypeArguments.errors.txt +++ b/tests/baselines/reference/instantiateGenericClassWithWrongNumberOfTypeArguments.errors.txt @@ -1,5 +1,5 @@ -tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGenericClassWithWrongNumberOfTypeArguments.ts(8,9): error TS2558: Expected 1 type arguments, but got 2. -tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGenericClassWithWrongNumberOfTypeArguments.ts(16,9): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGenericClassWithWrongNumberOfTypeArguments.ts(8,15): error TS2558: Expected 1 type arguments, but got 2. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGenericClassWithWrongNumberOfTypeArguments.ts(16,15): error TS2558: Expected 2 type arguments, but got 1. ==== tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGenericClassWithWrongNumberOfTypeArguments.ts (2 errors) ==== @@ -11,7 +11,7 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGeneri } var c = new C(); - ~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~ !!! error TS2558: Expected 1 type arguments, but got 2. class D { @@ -21,5 +21,5 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateGeneri // BUG 794238 var d = new D(); - ~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. \ No newline at end of file diff --git a/tests/baselines/reference/instantiateNonGenericTypeWithTypeArguments.errors.txt b/tests/baselines/reference/instantiateNonGenericTypeWithTypeArguments.errors.txt index 196af18aa9a..5e70aa31249 100644 --- a/tests/baselines/reference/instantiateNonGenericTypeWithTypeArguments.errors.txt +++ b/tests/baselines/reference/instantiateNonGenericTypeWithTypeArguments.errors.txt @@ -1,8 +1,8 @@ -tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(8,9): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(8,15): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(11,9): error TS2350: Only a void function can be called with the 'new' keyword. -tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(11,9): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(11,17): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(14,10): error TS2350: Only a void function can be called with the 'new' keyword. -tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(14,10): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(14,16): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGenericTypeWithTypeArguments.ts(18,10): error TS2347: Untyped function calls may not accept type arguments. @@ -15,21 +15,21 @@ tests/cases/conformance/types/typeParameters/typeArgumentLists/instantiateNonGen } var c = new C(); - ~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. function Foo(): void { } var r = new Foo(); ~~~~~~~~~~~~~~~~~ !!! error TS2350: Only a void function can be called with the 'new' keyword. - ~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var f: { (): void }; var r2 = new f(); ~~~~~~~~~~~~~~~ !!! error TS2350: Only a void function can be called with the 'new' keyword. - ~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var a: any; diff --git a/tests/baselines/reference/mismatchedExplicitTypeParameterAndArgumentType.errors.txt b/tests/baselines/reference/mismatchedExplicitTypeParameterAndArgumentType.errors.txt index 9b95b143d64..f96dc69ee33 100644 --- a/tests/baselines/reference/mismatchedExplicitTypeParameterAndArgumentType.errors.txt +++ b/tests/baselines/reference/mismatchedExplicitTypeParameterAndArgumentType.errors.txt @@ -1,7 +1,7 @@ tests/cases/compiler/mismatchedExplicitTypeParameterAndArgumentType.ts(10,30): error TS2345: Argument of type '(string | number)[]' is not assignable to parameter of type 'number[]'. Type 'string | number' is not assignable to type 'number'. Type 'string' is not assignable to type 'number'. -tests/cases/compiler/mismatchedExplicitTypeParameterAndArgumentType.ts(11,11): error TS2558: Expected 2 type arguments, but got 1. +tests/cases/compiler/mismatchedExplicitTypeParameterAndArgumentType.ts(11,15): error TS2558: Expected 2 type arguments, but got 1. ==== tests/cases/compiler/mismatchedExplicitTypeParameterAndArgumentType.ts (2 errors) ==== @@ -20,6 +20,6 @@ tests/cases/compiler/mismatchedExplicitTypeParameterAndArgumentType.ts(11,11): e !!! error TS2345: Type 'string | number' is not assignable to type 'number'. !!! error TS2345: Type 'string' is not assignable to type 'number'. var r7b = map([1, ""], (x) => x.toString()); // error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 2 type arguments, but got 1. var r8 = map([1, ""], (x) => x.toString()); \ No newline at end of file diff --git a/tests/baselines/reference/overloadResolution.errors.txt b/tests/baselines/reference/overloadResolution.errors.txt index 37cea935e66..6710f697b3c 100644 --- a/tests/baselines/reference/overloadResolution.errors.txt +++ b/tests/baselines/reference/overloadResolution.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(27,5): error TS2345: Argument of type '{}' is not assignable to parameter of type 'number'. tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(41,11): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. -tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(63,1): error TS2558: Expected 1-3 type arguments, but got 4. +tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(63,5): error TS2558: Expected 1-3 type arguments, but got 4. tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(70,21): error TS2345: Argument of type '3' is not assignable to parameter of type 'string'. tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(71,21): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(81,5): error TS2344: Type 'boolean' does not satisfy the constraint 'number'. @@ -78,7 +78,7 @@ tests/cases/conformance/expressions/functionCalls/overloadResolution.ts(91,22): // Generic overloads with differing arity called with type argument count that doesn't match any overload fn3(); // Error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 1-3 type arguments, but got 4. // Generic overloads with constraints called with type arguments that satisfy the constraints diff --git a/tests/baselines/reference/overloadResolutionClassConstructors.errors.txt b/tests/baselines/reference/overloadResolutionClassConstructors.errors.txt index 92f3a7cfa90..cfbc2605559 100644 --- a/tests/baselines/reference/overloadResolutionClassConstructors.errors.txt +++ b/tests/baselines/reference/overloadResolutionClassConstructors.errors.txt @@ -1,7 +1,7 @@ tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(27,9): error TS2345: Argument of type '{}' is not assignable to parameter of type 'number'. -tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(60,1): error TS2558: Expected 3 type arguments, but got 1. -tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(61,1): error TS2558: Expected 3 type arguments, but got 2. -tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(65,1): error TS2558: Expected 3 type arguments, but got 4. +tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(60,9): error TS2558: Expected 3 type arguments, but got 1. +tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(61,9): error TS2558: Expected 3 type arguments, but got 2. +tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(65,9): error TS2558: Expected 3 type arguments, but got 4. tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(73,25): error TS2345: Argument of type '3' is not assignable to parameter of type 'string'. tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(74,9): error TS2344: Type 'number' does not satisfy the constraint 'string'. tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstructors.ts(75,9): error TS2344: Type 'number' does not satisfy the constraint 'string'. @@ -77,16 +77,16 @@ tests/cases/conformance/expressions/functionCalls/overloadResolutionClassConstru // Generic overloads with differing arity called with type arguments matching each overload type parameter count new fn3(4); // Error - ~~~~~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 3 type arguments, but got 1. new fn3('', '', ''); // Error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~ !!! error TS2558: Expected 3 type arguments, but got 2. new fn3('', '', 3); // Generic overloads with differing arity called with type argument count that doesn't match any overload new fn3(); // Error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 3 type arguments, but got 4. // Generic overloads with constraints called with type arguments that satisfy the constraints diff --git a/tests/baselines/reference/overloadResolutionConstructors.errors.txt b/tests/baselines/reference/overloadResolutionConstructors.errors.txt index 9b09992228e..fe1c955651b 100644 --- a/tests/baselines/reference/overloadResolutionConstructors.errors.txt +++ b/tests/baselines/reference/overloadResolutionConstructors.errors.txt @@ -1,6 +1,6 @@ tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(27,9): error TS2345: Argument of type '{}' is not assignable to parameter of type 'number'. tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(43,15): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. -tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(67,1): error TS2558: Expected 1-3 type arguments, but got 4. +tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(67,9): error TS2558: Expected 1-3 type arguments, but got 4. tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(77,25): error TS2345: Argument of type '3' is not assignable to parameter of type 'string'. tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(78,25): error TS2345: Argument of type '""' is not assignable to parameter of type 'number'. tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors.ts(88,9): error TS2344: Type 'boolean' does not satisfy the constraint 'number'. @@ -82,7 +82,7 @@ tests/cases/conformance/expressions/functionCalls/overloadResolutionConstructors // Generic overloads with differing arity called with type argument count that doesn't match any overload new fn3(); // Error - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 1-3 type arguments, but got 4. // Generic overloads with constraints called with type arguments that satisfy the constraints diff --git a/tests/baselines/reference/overloadsAndTypeArgumentArityErrors.errors.txt b/tests/baselines/reference/overloadsAndTypeArgumentArityErrors.errors.txt index 52bcaaf790b..45a5616d188 100644 --- a/tests/baselines/reference/overloadsAndTypeArgumentArityErrors.errors.txt +++ b/tests/baselines/reference/overloadsAndTypeArgumentArityErrors.errors.txt @@ -1,6 +1,6 @@ -tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(5,1): error TS2558: Expected 0-2 type arguments, but got 3. +tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(5,11): error TS2558: Expected 0-2 type arguments, but got 3. tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(6,1): error TS2350: Only a void function can be called with the 'new' keyword. -tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(6,1): error TS2558: Expected 0-2 type arguments, but got 3. +tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(6,15): error TS2558: Expected 0-2 type arguments, but got 3. ==== tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts (3 errors) ==== @@ -9,10 +9,10 @@ tests/cases/compiler/overloadsAndTypeArgumentArityErrors.ts(6,1): error TS2558: declare function Callbacks(flags?: string): void; Callbacks('s'); // wrong number of type arguments - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 0-2 type arguments, but got 3. new Callbacks('s'); // wrong number of type arguments ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2350: Only a void function can be called with the 'new' keyword. - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~~~~~~~~~~ !!! error TS2558: Expected 0-2 type arguments, but got 3. \ No newline at end of file diff --git a/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt b/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt index c13916fa39d..cd58ed117f6 100644 --- a/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt +++ b/tests/baselines/reference/parserConstructorAmbiguity3.errors.txt @@ -1,13 +1,13 @@ -tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,1): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,10): error TS2304: Cannot find name 'A'. +tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,10): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts(1,12): error TS1005: '(' expected. ==== tests/cases/conformance/parser/ecmascript5/Generics/parserConstructorAmbiguity3.ts (3 errors) ==== new Date - ~~~~~~~~~~~ -!!! error TS2558: Expected 0 type arguments, but got 1. ~ !!! error TS2304: Cannot find name 'A'. + ~ +!!! error TS2558: Expected 0 type arguments, but got 1. !!! error TS1005: '(' expected. \ No newline at end of file diff --git a/tests/baselines/reference/thisExpressionInCallExpressionWithTypeArguments.errors.txt b/tests/baselines/reference/thisExpressionInCallExpressionWithTypeArguments.errors.txt index 1dac3478c63..22f4632cef8 100644 --- a/tests/baselines/reference/thisExpressionInCallExpressionWithTypeArguments.errors.txt +++ b/tests/baselines/reference/thisExpressionInCallExpressionWithTypeArguments.errors.txt @@ -1,10 +1,10 @@ -tests/cases/compiler/thisExpressionInCallExpressionWithTypeArguments.ts(2,20): error TS2558: Expected 1 type arguments, but got 2. +tests/cases/compiler/thisExpressionInCallExpressionWithTypeArguments.ts(2,32): error TS2558: Expected 1 type arguments, but got 2. ==== tests/cases/compiler/thisExpressionInCallExpressionWithTypeArguments.ts (1 errors) ==== class C { public foo() { [1,2,3].map((x) => { return this; })} - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + ~~~~~~~ !!! error TS2558: Expected 1 type arguments, but got 2. } \ No newline at end of file diff --git a/tests/baselines/reference/tooManyTypeParameters1.errors.txt b/tests/baselines/reference/tooManyTypeParameters1.errors.txt index 7997e68b575..b966225bef5 100644 --- a/tests/baselines/reference/tooManyTypeParameters1.errors.txt +++ b/tests/baselines/reference/tooManyTypeParameters1.errors.txt @@ -1,23 +1,23 @@ -tests/cases/compiler/tooManyTypeParameters1.ts(2,1): error TS2558: Expected 1 type arguments, but got 2. -tests/cases/compiler/tooManyTypeParameters1.ts(5,1): error TS2558: Expected 1 type arguments, but got 2. -tests/cases/compiler/tooManyTypeParameters1.ts(8,9): error TS2558: Expected 1 type arguments, but got 2. +tests/cases/compiler/tooManyTypeParameters1.ts(2,3): error TS2558: Expected 1 type arguments, but got 2. +tests/cases/compiler/tooManyTypeParameters1.ts(5,3): error TS2558: Expected 1 type arguments, but got 2. +tests/cases/compiler/tooManyTypeParameters1.ts(8,15): error TS2558: Expected 1 type arguments, but got 2. tests/cases/compiler/tooManyTypeParameters1.ts(11,8): error TS2314: Generic type 'I' requires 1 type argument(s). ==== tests/cases/compiler/tooManyTypeParameters1.ts (4 errors) ==== function f() { } f(); - ~~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~~ !!! error TS2558: Expected 1 type arguments, but got 2. var x = () => {}; x(); - ~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~~~~~ !!! error TS2558: Expected 1 type arguments, but got 2. class C {} var c = new C(); - ~~~~~~~~~~~~~~~~~~ + ~~~~~~~~~ !!! error TS2558: Expected 1 type arguments, but got 2. interface I {} diff --git a/tests/baselines/reference/typeArgumentsOnFunctionsWithNoTypeParameters.errors.txt b/tests/baselines/reference/typeArgumentsOnFunctionsWithNoTypeParameters.errors.txt index 5c916331950..a162b997c0f 100644 --- a/tests/baselines/reference/typeArgumentsOnFunctionsWithNoTypeParameters.errors.txt +++ b/tests/baselines/reference/typeArgumentsOnFunctionsWithNoTypeParameters.errors.txt @@ -1,18 +1,18 @@ -tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts(2,13): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts(2,15): error TS2558: Expected 0 type arguments, but got 1. tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts(3,15): error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. -tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts(4,13): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts(4,15): error TS2558: Expected 0 type arguments, but got 1. ==== tests/cases/compiler/typeArgumentsOnFunctionsWithNoTypeParameters.ts (3 errors) ==== function foo(f: (v: T) => U) { var r1 = f(1); - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r2 = f(1); ~ !!! error TS2345: Argument of type '1' is not assignable to parameter of type 'T'. var r3 = f(null); - ~~~~~~~~~~~~ + ~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var r4 = f(null); } diff --git a/tests/baselines/reference/typeAssertions.errors.txt b/tests/baselines/reference/typeAssertions.errors.txt index 45f20437860..cefc15b8e23 100644 --- a/tests/baselines/reference/typeAssertions.errors.txt +++ b/tests/baselines/reference/typeAssertions.errors.txt @@ -1,4 +1,4 @@ -tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(5,5): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(5,9): error TS2558: Expected 0 type arguments, but got 1. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(31,12): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeBase'. Property 'p' is missing in type 'SomeOther'. tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(35,15): error TS2352: Type 'SomeOther' cannot be converted to type 'SomeDerived'. @@ -29,7 +29,7 @@ tests/cases/conformance/expressions/typeAssertions/typeAssertions.ts(48,50): err function fn2(t: any) { } fn1(fn2(4)); // Error - ~~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var a: any; diff --git a/tests/baselines/reference/untypedFunctionCallsWithTypeParameters1.errors.txt b/tests/baselines/reference/untypedFunctionCallsWithTypeParameters1.errors.txt index 6b6594fc1ed..d1a1b0f08f9 100644 --- a/tests/baselines/reference/untypedFunctionCallsWithTypeParameters1.errors.txt +++ b/tests/baselines/reference/untypedFunctionCallsWithTypeParameters1.errors.txt @@ -1,20 +1,20 @@ -tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(3,10): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(3,12): error TS2558: Expected 0 type arguments, but got 1. tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(5,10): error TS2347: Untyped function calls may not accept type arguments. tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(8,10): error TS2347: Untyped function calls may not accept type arguments. tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(10,7): error TS2420: Class 'C' incorrectly implements interface 'Function'. Property 'apply' is missing in type 'C'. tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(18,10): error TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'C' has no compatible call signatures. tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(22,10): error TS2347: Untyped function calls may not accept type arguments. -tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(28,10): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(35,1): error TS2558: Expected 0 type arguments, but got 1. -tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(41,1): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(28,12): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(35,4): error TS2558: Expected 0 type arguments, but got 1. +tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(41,4): error TS2558: Expected 0 type arguments, but got 1. ==== tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts (9 errors) ==== // none of these function calls should be allowed var x = function () { return; }; var r1 = x(); - ~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. var y: any = x; var r2 = y(); @@ -52,7 +52,7 @@ tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(41,1): error TS2 } var z: I; var r6 = z(1); // error - ~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. interface callable2 { @@ -61,7 +61,7 @@ tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(41,1): error TS2 var c4: callable2; c4(1); - ~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. interface callable3 { (a: T): T; @@ -69,7 +69,7 @@ tests/cases/compiler/untypedFunctionCallsWithTypeParameters1.ts(41,1): error TS2 var c5: callable3; c5(1); // error - ~~~~~~~~~~~~~ + ~~~~~~ !!! error TS2558: Expected 0 type arguments, but got 1. \ No newline at end of file From 1cc164b330929860f520cbad27e190cda621e00d Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Wed, 24 Jan 2018 12:05:44 -0800 Subject: [PATCH 089/100] Update version (#21319) --- package.json | 2 +- src/compiler/core.ts | 2 +- tests/baselines/reference/api/tsserverlibrary.d.ts | 2 +- tests/baselines/reference/api/typescript.d.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index fb089b64918..d0ec2d70ce2 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "http://typescriptlang.org/", - "version": "2.7.0", + "version": "2.8.0", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ diff --git a/src/compiler/core.ts b/src/compiler/core.ts index ae506fd616a..cefa9367b48 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -4,7 +4,7 @@ namespace ts { // WARNING: The script `configureNightly.ts` uses a regexp to parse out these values. // If changing the text in this section, be sure to test `configureNightly` too. - export const versionMajorMinor = "2.7"; + export const versionMajorMinor = "2.8"; /** The version of the TypeScript compiler release */ export const version = `${versionMajorMinor}.0`; } diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index cd1b577b711..a2ded98383e 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2808,7 +2808,7 @@ declare namespace ts { } } declare namespace ts { - const versionMajorMinor = "2.7"; + const versionMajorMinor = "2.8"; /** The version of the TypeScript compiler release */ const version: string; } diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 3cbca96f8fa..2cf666b1cf3 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2808,7 +2808,7 @@ declare namespace ts { } } declare namespace ts { - const versionMajorMinor = "2.7"; + const versionMajorMinor = "2.8"; /** The version of the TypeScript compiler release */ const version: string; } From ba797f2c50bd6bf211950bef932cb8fb64ec7cb2 Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Wed, 24 Jan 2018 12:37:45 -0800 Subject: [PATCH 090/100] Add flag to skip qualification check when symbol is already in the process of being qualified (#21337) --- src/compiler/checker.ts | 19 +++--- ...tionExportStarShadowingGlobalIsNameable.js | 60 +++++++++++++++++++ ...xportStarShadowingGlobalIsNameable.symbols | 49 +++++++++++++++ ...nExportStarShadowingGlobalIsNameable.types | 50 ++++++++++++++++ ...tionExportStarShadowingGlobalIsNameable.ts | 24 ++++++++ 5 files changed, 194 insertions(+), 8 deletions(-) create mode 100644 tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.js create mode 100644 tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.symbols create mode 100644 tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.types create mode 100644 tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index bb1fbbdd526..f3658c645db 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2424,12 +2424,15 @@ namespace ts { const visitedSymbolTables: SymbolTable[] = []; return forEachSymbolTableInScope(enclosingDeclaration, getAccessibleSymbolChainFromSymbolTable); - function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable): Symbol[] | undefined { + /** + * @param {ignoreQualification} boolean Set when a symbol is being looked for through the exports of another symbol (meaning we have a route to qualify it already) + */ + function getAccessibleSymbolChainFromSymbolTable(symbols: SymbolTable, ignoreQualification?: boolean): Symbol[] | undefined { if (!pushIfUnique(visitedSymbolTables, symbols)) { return undefined; } - const result = trySymbolTable(symbols); + const result = trySymbolTable(symbols, ignoreQualification); visitedSymbolTables.pop(); return result; } @@ -2441,22 +2444,22 @@ namespace ts { !!getAccessibleSymbolChain(symbolFromSymbolTable.parent, enclosingDeclaration, getQualifiedLeftMeaning(meaning), useOnlyExternalAliasing); } - function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol) { + function isAccessible(symbolFromSymbolTable: Symbol, resolvedAliasSymbol?: Symbol, ignoreQualification?: boolean) { return symbol === (resolvedAliasSymbol || symbolFromSymbolTable) && // if the symbolFromSymbolTable is not external module (it could be if it was determined as ambient external module and would be in globals table) // and if symbolFromSymbolTable or alias resolution matches the symbol, // check the symbol can be qualified, it is only then this symbol is accessible !some(symbolFromSymbolTable.declarations, hasExternalModuleSymbol) && - canQualifySymbol(symbolFromSymbolTable, meaning); + (ignoreQualification || canQualifySymbol(symbolFromSymbolTable, meaning)); } function isUMDExportSymbol(symbol: Symbol) { return symbol && symbol.declarations && symbol.declarations[0] && isNamespaceExportDeclaration(symbol.declarations[0]); } - function trySymbolTable(symbols: SymbolTable) { + function trySymbolTable(symbols: SymbolTable, ignoreQualification: boolean | undefined) { // If symbol is directly available by its name in the symbol table - if (isAccessible(symbols.get(symbol.escapedName))) { + if (isAccessible(symbols.get(symbol.escapedName), /*resolvedAliasSymbol*/ undefined, ignoreQualification)) { return [symbol]; } @@ -2469,14 +2472,14 @@ namespace ts { && (!useOnlyExternalAliasing || some(symbolFromSymbolTable.declarations, isExternalModuleImportEqualsDeclaration))) { const resolvedImportedSymbol = resolveAlias(symbolFromSymbolTable); - if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol)) { + if (isAccessible(symbolFromSymbolTable, resolvedImportedSymbol, ignoreQualification)) { return [symbolFromSymbolTable]; } // Look in the exported members, if we can find accessibleSymbolChain, symbol is accessible using this chain // but only if the symbolFromSymbolTable can be qualified const candidateTable = getExportsOfSymbol(resolvedImportedSymbol); - const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable); + const accessibleSymbolsFromExports = candidateTable && getAccessibleSymbolChainFromSymbolTable(candidateTable, /*ignoreQualification*/ true); if (accessibleSymbolsFromExports && canQualifySymbol(symbolFromSymbolTable, getQualifiedLeftMeaning(meaning))) { return [symbolFromSymbolTable].concat(accessibleSymbolsFromExports); } diff --git a/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.js b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.js new file mode 100644 index 00000000000..77b1b5f8ff4 --- /dev/null +++ b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.js @@ -0,0 +1,60 @@ +//// [tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts] //// + +//// [index.ts] +export * from "./account"; + +//// [account.ts] +export interface Account { + myAccNum: number; +} +interface Account2 { + myAccNum: number; +} +export { Account2 as Acc }; + +//// [index.ts] +declare global { + interface Account { + someProp: number; + } + interface Acc { + someProp: number; + } +} +import * as model from "./model"; +export const func = (account: model.Account, acc2: model.Acc) => {}; + + +//// [account.js] +"use strict"; +exports.__esModule = true; +//// [index.js] +"use strict"; +exports.__esModule = true; +//// [index.js] +"use strict"; +exports.__esModule = true; +exports.func = function (account, acc2) { }; + + +//// [account.d.ts] +export interface Account { + myAccNum: number; +} +interface Account2 { + myAccNum: number; +} +export { Account2 as Acc }; +//// [index.d.ts] +export * from "./account"; +//// [index.d.ts] +declare global { + interface Account { + someProp: number; + } + interface Acc { + someProp: number; + } +} +import * as model from "./model"; +export declare const func: (account: model.Account, acc2: model.Acc) => void; diff --git a/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.symbols b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.symbols new file mode 100644 index 00000000000..add5fce7476 --- /dev/null +++ b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.symbols @@ -0,0 +1,49 @@ +=== tests/cases/compiler/model/index.ts === +export * from "./account"; +No type information for this code. +No type information for this code.=== tests/cases/compiler/model/account.ts === +export interface Account { +>Account : Symbol(Account, Decl(account.ts, 0, 0)) + + myAccNum: number; +>myAccNum : Symbol(Account.myAccNum, Decl(account.ts, 0, 26)) +} +interface Account2 { +>Account2 : Symbol(Account2, Decl(account.ts, 2, 1)) + + myAccNum: number; +>myAccNum : Symbol(Account2.myAccNum, Decl(account.ts, 3, 20)) +} +export { Account2 as Acc }; +>Account2 : Symbol(Acc, Decl(account.ts, 6, 8)) +>Acc : Symbol(Acc, Decl(account.ts, 6, 8)) + +=== tests/cases/compiler/index.ts === +declare global { +>global : Symbol(global, Decl(index.ts, 0, 0)) + + interface Account { +>Account : Symbol(Account, Decl(index.ts, 0, 16)) + + someProp: number; +>someProp : Symbol(Account.someProp, Decl(index.ts, 1, 23)) + } + interface Acc { +>Acc : Symbol(Acc, Decl(index.ts, 3, 5)) + + someProp: number; +>someProp : Symbol(Acc.someProp, Decl(index.ts, 4, 19)) + } +} +import * as model from "./model"; +>model : Symbol(model, Decl(index.ts, 8, 6)) + +export const func = (account: model.Account, acc2: model.Acc) => {}; +>func : Symbol(func, Decl(index.ts, 9, 12)) +>account : Symbol(account, Decl(index.ts, 9, 21)) +>model : Symbol(model, Decl(index.ts, 8, 6)) +>Account : Symbol(model.Account, Decl(account.ts, 0, 0)) +>acc2 : Symbol(acc2, Decl(index.ts, 9, 44)) +>model : Symbol(model, Decl(index.ts, 8, 6)) +>Acc : Symbol(model.Acc, Decl(account.ts, 6, 8)) + diff --git a/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.types b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.types new file mode 100644 index 00000000000..af27f7e3678 --- /dev/null +++ b/tests/baselines/reference/moduleDeclarationExportStarShadowingGlobalIsNameable.types @@ -0,0 +1,50 @@ +=== tests/cases/compiler/model/index.ts === +export * from "./account"; +No type information for this code. +No type information for this code.=== tests/cases/compiler/model/account.ts === +export interface Account { +>Account : Account + + myAccNum: number; +>myAccNum : number +} +interface Account2 { +>Account2 : Account2 + + myAccNum: number; +>myAccNum : number +} +export { Account2 as Acc }; +>Account2 : any +>Acc : any + +=== tests/cases/compiler/index.ts === +declare global { +>global : any + + interface Account { +>Account : Account + + someProp: number; +>someProp : number + } + interface Acc { +>Acc : Acc + + someProp: number; +>someProp : number + } +} +import * as model from "./model"; +>model : typeof model + +export const func = (account: model.Account, acc2: model.Acc) => {}; +>func : (account: model.Account, acc2: model.Acc) => void +>(account: model.Account, acc2: model.Acc) => {} : (account: model.Account, acc2: model.Acc) => void +>account : model.Account +>model : any +>Account : model.Account +>acc2 : model.Acc +>model : any +>Acc : model.Acc + diff --git a/tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts b/tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts new file mode 100644 index 00000000000..9113e6e329b --- /dev/null +++ b/tests/cases/compiler/moduleDeclarationExportStarShadowingGlobalIsNameable.ts @@ -0,0 +1,24 @@ +// @declaration: true +// @filename: model/index.ts +export * from "./account"; + +// @filename: model/account.ts +export interface Account { + myAccNum: number; +} +interface Account2 { + myAccNum: number; +} +export { Account2 as Acc }; + +// @filename: index.ts +declare global { + interface Account { + someProp: number; + } + interface Acc { + someProp: number; + } +} +import * as model from "./model"; +export const func = (account: model.Account, acc2: model.Acc) => {}; From 08aa2653dd4609561c9ffe94e8aa5c349a4d12c5 Mon Sep 17 00:00:00 2001 From: Andrew Casey Date: Wed, 24 Jan 2018 12:46:13 -0800 Subject: [PATCH 091/100] Remove incorrect assert --- src/harness/unittests/extractRanges.ts | 6 ++++++ src/services/refactors/extractSymbol.ts | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index 2810f323abf..00fbf334b38 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -411,6 +411,12 @@ switch (x) { refactor.extractSymbol.Messages.cannotExtractRange.message ]); + testExtractRangeFailed("extractRangeFailed18", + `[#|{ 1;|] }`, + [ + refactor.extractSymbol.Messages.cannotExtractRange.message + ]); + testExtractRangeFailed("extract-method-not-for-token-expression-statement", `[#|a|]`, [refactor.extractSymbol.Messages.cannotExtractIdentifier.message]); }); } \ No newline at end of file diff --git a/src/services/refactors/extractSymbol.ts b/src/services/refactors/extractSymbol.ts index e8a2186aa8b..e3a2763b783 100644 --- a/src/services/refactors/extractSymbol.ts +++ b/src/services/refactors/extractSymbol.ts @@ -242,7 +242,6 @@ namespace ts.refactor.extractSymbol { // they will never find `start` in `start.parent.statements`. // Consider: We could support ranges like [|case 1:|] by refining them to just // the expression. - Debug.assert(isCaseClause(start.parent) && span.start < start.parent.expression.end); return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; } From e58391d9c53c22a1207c5cb2fe7ec21b5399887e Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 24 Jan 2018 15:06:00 -0800 Subject: [PATCH 092/100] In fourslash.ts, remove unused exports and use '{}' instead of 'any' (#21377) --- src/harness/fourslash.ts | 43 +++++++++++++--------------------------- 1 file changed, 14 insertions(+), 29 deletions(-) diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index bf5f46de248..75476669b59 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -23,7 +23,7 @@ namespace FourSlash { ts.disableIncrementalParsing = false; // Represents a parsed source file with metadata - export interface FourSlashFile { + interface FourSlashFile { // The contents of the file (with markers, etc stripped out) content: string; fileName: string; @@ -34,7 +34,7 @@ namespace FourSlash { } // Represents a set of parsed source files and options - export interface FourSlashData { + interface FourSlashData { // Global options (name/value pairs) globalOptions: Harness.TestCaseParser.CompilerSettings; @@ -59,7 +59,7 @@ namespace FourSlash { export interface Marker { fileName: string; position: number; - data?: any; + data?: {}; } export interface Range { @@ -89,21 +89,6 @@ namespace FourSlash { end: number; } - export import IndentStyle = ts.IndentStyle; - - const entityMap = ts.createMapFromTemplate({ - "&": "&", - "\"": """, - "'": "'", - "/": "/", - "<": "<", - ">": ">" - }); - - export function escapeXmlAttributeValue(s: string) { - return s.replace(/[&<>"'\/]/g, ch => entityMap.get(ch)); - } - // Name of testcase metadata including ts.CompilerOptions properties that will be used by globalOptions // To add additional option, add property into the testOptMetadataNames, refer the property in either globalMetadataNames or fileMetadataNames // Add cases into convertGlobalOptionsToCompilationsSettings function for the compiler to acknowledge such option from meta data @@ -1079,7 +1064,7 @@ namespace FourSlash { for (const reference of expectedReferences) { const { fileName, start, end } = reference; if (reference.marker && reference.marker.data) { - const { isWriteAccess, isDefinition } = reference.marker.data; + const { isWriteAccess, isDefinition } = reference.marker.data as { isWriteAccess?: boolean, isDefinition?: boolean }; this.verifyReferencesWorker(actualReferences, fileName, start, end, isWriteAccess, isDefinition); } else { @@ -1108,7 +1093,16 @@ namespace FourSlash { } const fullExpected = ts.map(parts, ({ definition, ranges }) => ({ definition: typeof definition === "string" ? definition : { ...definition, range: textSpanFromRange(definition.range) }, - references: ranges.map(rangeToReferenceEntry), + references: ranges.map(r => { + const { isWriteAccess = false, isDefinition = false, isInString } = (r.marker && r.marker.data || {}) as { isWriteAccess?: boolean, isDefinition?: boolean, isInString?: true }; + return { + isWriteAccess, + isDefinition, + fileName: r.fileName, + textSpan: textSpanFromRange(r), + ...(isInString ? { isInString: true } : undefined), + }; + }), })); for (const startRange of toArray(startRanges)) { @@ -1122,15 +1116,6 @@ namespace FourSlash { }); this.assertObjectsEqual(fullActual, fullExpected); } - - function rangeToReferenceEntry(r: Range): ts.ReferenceEntry { - const { isWriteAccess, isDefinition, isInString } = (r.marker && r.marker.data) || { isWriteAccess: false, isDefinition: false, isInString: undefined }; - const result: ts.ReferenceEntry = { fileName: r.fileName, textSpan: textSpanFromRange(r), isWriteAccess: !!isWriteAccess, isDefinition: !!isDefinition }; - if (isInString !== undefined) { - result.isInString = isInString; - } - return result; - } } public verifyNoReferences(markerNameOrRange?: string | Range) { From d333d889c1d42a98deceb81545ac5a9a57448abe Mon Sep 17 00:00:00 2001 From: Andy Date: Wed, 24 Jan 2018 15:06:52 -0800 Subject: [PATCH 093/100] Test for (and fix) order of import fixes (#21398) --- src/harness/fourslash.ts | 8 +++---- src/services/codefixes/importFixes.ts | 2 +- .../fourslash/completionsImportBaseUrl.ts | 21 +++++++++++++++++++ .../importNameCodeFixExistingImport2.ts | 4 ++-- .../importNameCodeFixNewImportBaseUrl0.ts | 6 +++--- .../importNameCodeFixOptionalImport0.ts | 6 +++--- .../importNameCodeFixOptionalImport1.ts | 10 ++++----- 7 files changed, 38 insertions(+), 19 deletions(-) create mode 100644 tests/cases/fourslash/completionsImportBaseUrl.ts diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index 75476669b59..98e8e61eef5 100644 --- a/src/harness/fourslash.ts +++ b/src/harness/fourslash.ts @@ -2572,12 +2572,10 @@ Actual: ${stringify(fullActual)}`); actualTextArray.push(text); scriptInfo.updateContent(originalContent); } - const sortedExpectedArray = expectedTextArray.sort(); - const sortedActualArray = actualTextArray.sort(); - if (sortedExpectedArray.length !== sortedActualArray.length) { - this.raiseError(`Expected ${sortedExpectedArray.length} import fixes, got ${sortedActualArray.length}`); + if (expectedTextArray.length !== actualTextArray.length) { + this.raiseError(`Expected ${expectedTextArray.length} import fixes, got ${actualTextArray.length}`); } - ts.zipWith(sortedExpectedArray, sortedActualArray, (expected, actual, index) => { + ts.zipWith(expectedTextArray, actualTextArray, (expected, actual, index) => { if (expected !== actual) { this.raiseError(`Import fix at index ${index} doesn't match.\n${showTextDiff(expected, actual)}`); } diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 4c536ce3fd2..ac88d98eba5 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -390,7 +390,7 @@ namespace ts.codefix { In this case we should prefer using the relative path "../a" instead of the baseUrl path "foo/a". */ const pathFromSourceToBaseUrl = getRelativePath(baseUrl, sourceDirectory, getCanonicalFileName); - const relativeFirst = getRelativePathNParents(pathFromSourceToBaseUrl) < getRelativePathNParents(relativePath); + const relativeFirst = getRelativePathNParents(relativePath) < getRelativePathNParents(pathFromSourceToBaseUrl); return relativeFirst ? [relativePath, importRelativeToBaseUrl] : [importRelativeToBaseUrl, relativePath]; })); // Only return results for the re-export with the shortest possible path (and also give the other path even if that's long.) diff --git a/tests/cases/fourslash/completionsImportBaseUrl.ts b/tests/cases/fourslash/completionsImportBaseUrl.ts new file mode 100644 index 00000000000..e5f5b07b9f4 --- /dev/null +++ b/tests/cases/fourslash/completionsImportBaseUrl.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: /tsconfig.json +////{ +//// "compilerOptions": { +//// "baseUrl": "." +//// } +////} + +// @Filename: /src/a.ts +////export const foo = 0; + +// @Filename: /src/b.ts +////fo/**/ + +// Test that it prefers a relative import (see sourceDisplay). +goTo.marker(""); +verify.completionListContains({ name: "foo", source: "/src/a" }, "const foo: 0", "", "const", undefined, /*hasAction*/ true, { + includeExternalModuleExports: true, + sourceDisplay: "./a", +}); diff --git a/tests/cases/fourslash/importNameCodeFixExistingImport2.ts b/tests/cases/fourslash/importNameCodeFixExistingImport2.ts index 1146f24aeae..dbf34f4389e 100644 --- a/tests/cases/fourslash/importNameCodeFixExistingImport2.ts +++ b/tests/cases/fourslash/importNameCodeFixExistingImport2.ts @@ -9,8 +9,8 @@ verify.importFixAtPosition([ `import * as ns from "./module"; +ns.f1();`, +`import * as ns from "./module"; import { f1 } from "./module"; f1();`, -`import * as ns from "./module"; -ns.f1();`, ]); diff --git a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts index bc852b5c47c..fba8875a610 100644 --- a/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts +++ b/tests/cases/fourslash/importNameCodeFixNewImportBaseUrl0.ts @@ -13,10 +13,10 @@ //// export function f1() { }; verify.importFixAtPosition([ +`import { f1 } from "b"; + +f1();`, `import { f1 } from "./a/b"; f1();`, -`import { f1 } from "b"; - -f1();` ]); diff --git a/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts b/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts index 218ec4fabb7..3fbae01fab0 100644 --- a/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts +++ b/tests/cases/fourslash/importNameCodeFixOptionalImport0.ts @@ -12,9 +12,9 @@ verify.importFixAtPosition([ `import * as ns from "./foo"; -import { foo } from "./foo"; -foo();`, +ns.foo();`, `import * as ns from "./foo"; -ns.foo();`, +import { foo } from "./foo"; +foo();`, ]); diff --git a/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts b/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts index 7a3d19e1532..11204b3c4d2 100644 --- a/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts +++ b/tests/cases/fourslash/importNameCodeFixOptionalImport1.ts @@ -7,14 +7,14 @@ //// export function foo() {}; // @Filename: a/foo.ts -//// export { foo } from "bar"; +//// export { foo } from "bar"; verify.importFixAtPosition([ -`import { foo } from "./foo"; - -foo();`, - `import { foo } from "bar"; +foo();`, + +`import { foo } from "./foo"; + foo();`, ]); From fe0c461d9139c01372e6b6816f85c38cfd20ada2 Mon Sep 17 00:00:00 2001 From: csigs Date: Wed, 24 Jan 2018 23:10:36 +0000 Subject: [PATCH 094/100] LEGO: check in for master to temporary branch. --- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- .../diagnosticMessages/diagnosticMessages.generated.json.lcl | 2 +- 13 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7ae7fab1b3b..5d67c5c6cdc 100644 --- a/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7cc0cf16633..c7e02a6c2cc 100644 --- a/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/cht/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7ef4d27d021..3747e00c1f9 100644 --- a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -19,7 +19,7 @@ - + diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7fe309d50f8..4d8f2721a08 100644 --- a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 3ad2bbf63b2..517b05df202 100644 --- a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -19,7 +19,7 @@ - + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index 1262a3743cf..99776a461cc 100644 --- a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -19,7 +19,7 @@ - + diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index 7579d5ce794..e13e8191eb2 100644 --- a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 551a0e28806..1a892b757cb 100644 --- a/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/jpn/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl index d9b0532182c..b534bf56365 100644 --- a/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/kor/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -14,7 +14,7 @@ - + diff --git a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl index 400187f844a..c5473f41421 100644 --- a/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/plk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -7,7 +7,7 @@ - + diff --git a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl index b6290bed3fb..080b2e11402 100644 --- a/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/ptb/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -7,7 +7,7 @@ - + diff --git a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl index 46dce16bc93..bb0b07794a3 100644 --- a/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/rus/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -13,7 +13,7 @@ - + diff --git a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl index c1894d0db09..44a9f695fca 100644 --- a/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl +++ b/src/loc/lcl/trk/diagnosticMessages/diagnosticMessages.generated.json.lcl @@ -7,7 +7,7 @@ - + From 5698a6ab52d50f25f912bebe012ea8e99c28cc0e Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Thu, 25 Jan 2018 02:11:01 -0800 Subject: [PATCH 095/100] Fix loop labels for for..await --- src/compiler/transformers/esnext.ts | 2 +- .../reference/emitter.forAwait.es2015.js | 88 +++++++++ .../reference/emitter.forAwait.es2015.symbols | 30 +++ .../reference/emitter.forAwait.es2015.types | 34 ++++ .../reference/emitter.forAwait.es2017.js | 78 ++++++++ .../reference/emitter.forAwait.es2017.symbols | 30 +++ .../reference/emitter.forAwait.es2017.types | 34 ++++ .../reference/emitter.forAwait.es5.js | 182 ++++++++++++++++++ .../reference/emitter.forAwait.es5.symbols | 30 +++ .../reference/emitter.forAwait.es5.types | 34 ++++ .../reference/emitter.forAwait.esnext.js | 32 +++ .../reference/emitter.forAwait.esnext.symbols | 30 +++ .../reference/emitter.forAwait.esnext.types | 34 ++++ .../forAwait/emitter.forAwait.es2015.ts | 16 ++ .../forAwait/emitter.forAwait.es2017.ts | 16 ++ .../es5/forAwait/emitter.forAwait.es5.ts | 16 ++ .../forAwait/emitter.forAwait.esnext.ts | 16 ++ 17 files changed, 701 insertions(+), 1 deletion(-) diff --git a/src/compiler/transformers/esnext.ts b/src/compiler/transformers/esnext.ts index 5d46c8430d2..424371ad311 100644 --- a/src/compiler/transformers/esnext.ts +++ b/src/compiler/transformers/esnext.ts @@ -148,7 +148,7 @@ namespace ts { } function visitLabeledStatement(node: LabeledStatement) { - if (enclosingFunctionFlags & FunctionFlags.Async && enclosingFunctionFlags & FunctionFlags.Generator) { + if (enclosingFunctionFlags & FunctionFlags.Async) { const statement = unwrapInnermostStatementOfLabel(node); if (statement.kind === SyntaxKind.ForOfStatement && (statement).awaitModifier) { return visitForOfStatement(statement, node); diff --git a/tests/baselines/reference/emitter.forAwait.es2015.js b/tests/baselines/reference/emitter.forAwait.es2015.js index 323d62f0ac8..6b6bdd739fd 100644 --- a/tests/baselines/reference/emitter.forAwait.es2015.js +++ b/tests/baselines/reference/emitter.forAwait.es2015.js @@ -24,6 +24,22 @@ async function* f4() { for await (x of y) { } } +//// [file5.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +//// [file6.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} //// [file1.js] var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -161,3 +177,75 @@ function f4() { var e_1, _a; }); } +//// [file5.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +function f5() { + return __awaiter(this, void 0, void 0, function* () { + let y; + try { + outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield y_1.next(), !y_1_1.done;) { + const x = yield y_1_1.value; + continue outer; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield _a.call(y_1); + } + finally { if (e_1) throw e_1.error; } + } + var e_1, _a; + }); +} +//// [file6.js] +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +function f6() { + return __asyncGenerator(this, arguments, function* f6_1() { + let y; + try { + outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { + const x = yield __await(y_1_1.value); + continue outer; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1)); + } + finally { if (e_1) throw e_1.error; } + } + var e_1, _a; + }); +} diff --git a/tests/baselines/reference/emitter.forAwait.es2015.symbols b/tests/baselines/reference/emitter.forAwait.es2015.symbols index 6c4ed3b10e9..bc75908f601 100644 --- a/tests/baselines/reference/emitter.forAwait.es2015.symbols +++ b/tests/baselines/reference/emitter.forAwait.es2015.symbols @@ -48,3 +48,33 @@ async function* f4() { >y : Symbol(y, Decl(file4.ts, 1, 15)) } } +=== tests/cases/conformance/emitter/es2015/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : Symbol(f5, Decl(file5.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file5.ts, 3, 27)) +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + continue outer; + } +} +=== tests/cases/conformance/emitter/es2015/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : Symbol(f6, Decl(file6.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file6.ts, 3, 27)) +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + continue outer; + } +} diff --git a/tests/baselines/reference/emitter.forAwait.es2015.types b/tests/baselines/reference/emitter.forAwait.es2015.types index ac7321e43b4..e7d1c77f346 100644 --- a/tests/baselines/reference/emitter.forAwait.es2015.types +++ b/tests/baselines/reference/emitter.forAwait.es2015.types @@ -48,3 +48,37 @@ async function* f4() { >y : any } } +=== tests/cases/conformance/emitter/es2015/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : () => Promise + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} +=== tests/cases/conformance/emitter/es2015/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : () => AsyncIterableIterator + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} diff --git a/tests/baselines/reference/emitter.forAwait.es2017.js b/tests/baselines/reference/emitter.forAwait.es2017.js index 4b3770f02af..8eb21442558 100644 --- a/tests/baselines/reference/emitter.forAwait.es2017.js +++ b/tests/baselines/reference/emitter.forAwait.es2017.js @@ -24,6 +24,22 @@ async function* f4() { for await (x of y) { } } +//// [file5.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +//// [file6.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} //// [file1.js] var __asyncValues = (this && this.__asyncValues) || function (o) { @@ -141,3 +157,65 @@ function f4() { var e_1, _a; }); } +//// [file5.js] +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y; + try { + outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = await y_1.next(), !y_1_1.done;) { + const x = await y_1_1.value; + continue outer; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (y_1_1 && !y_1_1.done && (_a = y_1.return)) await _a.call(y_1); + } + finally { if (e_1) throw e_1.error; } + } + var e_1, _a; +} +//// [file6.js] +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +function f6() { + return __asyncGenerator(this, arguments, function* f6_1() { + let y; + try { + outer: for (var y_1 = __asyncValues(y), y_1_1; y_1_1 = yield __await(y_1.next()), !y_1_1.done;) { + const x = yield __await(y_1_1.value); + continue outer; + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (y_1_1 && !y_1_1.done && (_a = y_1.return)) yield __await(_a.call(y_1)); + } + finally { if (e_1) throw e_1.error; } + } + var e_1, _a; + }); +} diff --git a/tests/baselines/reference/emitter.forAwait.es2017.symbols b/tests/baselines/reference/emitter.forAwait.es2017.symbols index a08e31f5553..184d26c1266 100644 --- a/tests/baselines/reference/emitter.forAwait.es2017.symbols +++ b/tests/baselines/reference/emitter.forAwait.es2017.symbols @@ -48,3 +48,33 @@ async function* f4() { >y : Symbol(y, Decl(file4.ts, 1, 15)) } } +=== tests/cases/conformance/emitter/es2017/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : Symbol(f5, Decl(file5.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file5.ts, 3, 27)) +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + continue outer; + } +} +=== tests/cases/conformance/emitter/es2017/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : Symbol(f6, Decl(file6.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file6.ts, 3, 27)) +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + continue outer; + } +} diff --git a/tests/baselines/reference/emitter.forAwait.es2017.types b/tests/baselines/reference/emitter.forAwait.es2017.types index 0d886cca4cc..f1dffdd280a 100644 --- a/tests/baselines/reference/emitter.forAwait.es2017.types +++ b/tests/baselines/reference/emitter.forAwait.es2017.types @@ -48,3 +48,37 @@ async function* f4() { >y : any } } +=== tests/cases/conformance/emitter/es2017/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : () => Promise + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} +=== tests/cases/conformance/emitter/es2017/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : () => AsyncIterableIterator + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} diff --git a/tests/baselines/reference/emitter.forAwait.es5.js b/tests/baselines/reference/emitter.forAwait.es5.js index 2b073a3f43a..b4ea4ddcf54 100644 --- a/tests/baselines/reference/emitter.forAwait.es5.js +++ b/tests/baselines/reference/emitter.forAwait.es5.js @@ -24,6 +24,22 @@ async function* f4() { for await (x of y) { } } +//// [file5.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +//// [file6.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} //// [file1.js] var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { @@ -353,3 +369,169 @@ function f4() { }); }); } +//// [file5.js] +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +function f5() { + return __awaiter(this, void 0, void 0, function () { + var y, y_1, y_1_1, x, e_1_1, e_1, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + _b.trys.push([0, 6, 7, 12]); + y_1 = __asyncValues(y); + _b.label = 1; + case 1: return [4 /*yield*/, y_1.next()]; + case 2: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; + return [4 /*yield*/, y_1_1.value]; + case 3: + x = _b.sent(); + return [3 /*break*/, 4]; + case 4: return [3 /*break*/, 1]; + case 5: return [3 /*break*/, 12]; + case 6: + e_1_1 = _b.sent(); + e_1 = { error: e_1_1 }; + return [3 /*break*/, 12]; + case 7: + _b.trys.push([7, , 10, 11]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; + return [4 /*yield*/, _a.call(y_1)]; + case 8: + _b.sent(); + _b.label = 9; + case 9: return [3 /*break*/, 11]; + case 10: + if (e_1) throw e_1.error; + return [7 /*endfinally*/]; + case 11: return [7 /*endfinally*/]; + case 12: return [2 /*return*/]; + } + }); + }); +} +//// [file6.js] +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; + return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (_) try { + if (f = 1, y && (t = y[op[0] & 2 ? "return" : op[0] ? "throw" : "next"]) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [0, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +var __asyncValues = (this && this.__asyncValues) || function (o) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var m = o[Symbol.asyncIterator]; + return m ? m.call(o) : typeof __values === "function" ? __values(o) : o[Symbol.iterator](); +}; +var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); } +var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) { + if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined."); + var g = generator.apply(thisArg, _arguments || []), i, q = []; + return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i; + function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; } + function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } } + function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); } + function fulfill(value) { resume("next", value); } + function reject(value) { resume("throw", value); } + function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); } +}; +// https://github.com/Microsoft/TypeScript/issues/21363 +function f6() { + return __asyncGenerator(this, arguments, function f6_1() { + var y, y_1, y_1_1, x, e_1_1, e_1, _a; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + _b.trys.push([0, 6, 7, 12]); + y_1 = __asyncValues(y); + _b.label = 1; + case 1: return [4 /*yield*/, __await(y_1.next())]; + case 2: + if (!(y_1_1 = _b.sent(), !y_1_1.done)) return [3 /*break*/, 5]; + return [4 /*yield*/, __await(y_1_1.value)]; + case 3: + x = _b.sent(); + return [3 /*break*/, 4]; + case 4: return [3 /*break*/, 1]; + case 5: return [3 /*break*/, 12]; + case 6: + e_1_1 = _b.sent(); + e_1 = { error: e_1_1 }; + return [3 /*break*/, 12]; + case 7: + _b.trys.push([7, , 10, 11]); + if (!(y_1_1 && !y_1_1.done && (_a = y_1.return))) return [3 /*break*/, 9]; + return [4 /*yield*/, __await(_a.call(y_1))]; + case 8: + _b.sent(); + _b.label = 9; + case 9: return [3 /*break*/, 11]; + case 10: + if (e_1) throw e_1.error; + return [7 /*endfinally*/]; + case 11: return [7 /*endfinally*/]; + case 12: return [2 /*return*/]; + } + }); + }); +} diff --git a/tests/baselines/reference/emitter.forAwait.es5.symbols b/tests/baselines/reference/emitter.forAwait.es5.symbols index 40c5562453c..05fb915c0fc 100644 --- a/tests/baselines/reference/emitter.forAwait.es5.symbols +++ b/tests/baselines/reference/emitter.forAwait.es5.symbols @@ -48,3 +48,33 @@ async function* f4() { >y : Symbol(y, Decl(file4.ts, 1, 15)) } } +=== tests/cases/conformance/emitter/es5/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : Symbol(f5, Decl(file5.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file5.ts, 3, 27)) +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + continue outer; + } +} +=== tests/cases/conformance/emitter/es5/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : Symbol(f6, Decl(file6.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file6.ts, 3, 27)) +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + continue outer; + } +} diff --git a/tests/baselines/reference/emitter.forAwait.es5.types b/tests/baselines/reference/emitter.forAwait.es5.types index 8c8f6dec723..fe0a49a9016 100644 --- a/tests/baselines/reference/emitter.forAwait.es5.types +++ b/tests/baselines/reference/emitter.forAwait.es5.types @@ -48,3 +48,37 @@ async function* f4() { >y : any } } +=== tests/cases/conformance/emitter/es5/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : () => Promise + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} +=== tests/cases/conformance/emitter/es5/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : () => AsyncIterableIterator + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} diff --git a/tests/baselines/reference/emitter.forAwait.esnext.js b/tests/baselines/reference/emitter.forAwait.esnext.js index 7a6c6837c9a..0042ae38fbd 100644 --- a/tests/baselines/reference/emitter.forAwait.esnext.js +++ b/tests/baselines/reference/emitter.forAwait.esnext.js @@ -24,6 +24,22 @@ async function* f4() { for await (x of y) { } } +//// [file5.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +//// [file6.ts] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} //// [file1.js] async function f1() { @@ -49,3 +65,19 @@ async function* f4() { for await (x of y) { } } +//// [file5.js] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y; + outer: for await (const x of y) { + continue outer; + } +} +//// [file6.js] +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y; + outer: for await (const x of y) { + continue outer; + } +} diff --git a/tests/baselines/reference/emitter.forAwait.esnext.symbols b/tests/baselines/reference/emitter.forAwait.esnext.symbols index 137ebed0802..2910738102b 100644 --- a/tests/baselines/reference/emitter.forAwait.esnext.symbols +++ b/tests/baselines/reference/emitter.forAwait.esnext.symbols @@ -48,3 +48,33 @@ async function* f4() { >y : Symbol(y, Decl(file4.ts, 1, 15)) } } +=== tests/cases/conformance/emitter/esnext/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : Symbol(f5, Decl(file5.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file5.ts, 3, 27)) +>y : Symbol(y, Decl(file5.ts, 2, 7)) + + continue outer; + } +} +=== tests/cases/conformance/emitter/esnext/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : Symbol(f6, Decl(file6.ts, 0, 0)) + + let y: any; +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + outer: for await (const x of y) { +>x : Symbol(x, Decl(file6.ts, 3, 27)) +>y : Symbol(y, Decl(file6.ts, 2, 7)) + + continue outer; + } +} diff --git a/tests/baselines/reference/emitter.forAwait.esnext.types b/tests/baselines/reference/emitter.forAwait.esnext.types index ae52bd1080d..6339aeebeff 100644 --- a/tests/baselines/reference/emitter.forAwait.esnext.types +++ b/tests/baselines/reference/emitter.forAwait.esnext.types @@ -48,3 +48,37 @@ async function* f4() { >y : any } } +=== tests/cases/conformance/emitter/esnext/forAwait/file5.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { +>f5 : () => Promise + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} +=== tests/cases/conformance/emitter/esnext/forAwait/file6.ts === +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { +>f6 : () => AsyncIterableIterator + + let y: any; +>y : any + + outer: for await (const x of y) { +>outer : any +>x : any +>y : any + + continue outer; +>outer : any + } +} diff --git a/tests/cases/conformance/emitter/es2015/forAwait/emitter.forAwait.es2015.ts b/tests/cases/conformance/emitter/es2015/forAwait/emitter.forAwait.es2015.ts index 2069f08af30..64b44e901e2 100644 --- a/tests/cases/conformance/emitter/es2015/forAwait/emitter.forAwait.es2015.ts +++ b/tests/cases/conformance/emitter/es2015/forAwait/emitter.forAwait.es2015.ts @@ -23,4 +23,20 @@ async function* f4() { let x: any, y: any; for await (x of y) { } +} +// @filename: file5.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +// @filename: file6.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } } \ No newline at end of file diff --git a/tests/cases/conformance/emitter/es2017/forAwait/emitter.forAwait.es2017.ts b/tests/cases/conformance/emitter/es2017/forAwait/emitter.forAwait.es2017.ts index f2d1f46873d..f9d9fc3d4d9 100644 --- a/tests/cases/conformance/emitter/es2017/forAwait/emitter.forAwait.es2017.ts +++ b/tests/cases/conformance/emitter/es2017/forAwait/emitter.forAwait.es2017.ts @@ -23,4 +23,20 @@ async function* f4() { let x: any, y: any; for await (x of y) { } +} +// @filename: file5.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +// @filename: file6.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } } \ No newline at end of file diff --git a/tests/cases/conformance/emitter/es5/forAwait/emitter.forAwait.es5.ts b/tests/cases/conformance/emitter/es5/forAwait/emitter.forAwait.es5.ts index beb9ac9c2ca..a441b0da2bd 100644 --- a/tests/cases/conformance/emitter/es5/forAwait/emitter.forAwait.es5.ts +++ b/tests/cases/conformance/emitter/es5/forAwait/emitter.forAwait.es5.ts @@ -23,4 +23,20 @@ async function* f4() { let x: any, y: any; for await (x of y) { } +} +// @filename: file5.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +// @filename: file6.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } } \ No newline at end of file diff --git a/tests/cases/conformance/emitter/esnext/forAwait/emitter.forAwait.esnext.ts b/tests/cases/conformance/emitter/esnext/forAwait/emitter.forAwait.esnext.ts index 2f532f55ed5..ea1b030599e 100644 --- a/tests/cases/conformance/emitter/esnext/forAwait/emitter.forAwait.esnext.ts +++ b/tests/cases/conformance/emitter/esnext/forAwait/emitter.forAwait.esnext.ts @@ -23,4 +23,20 @@ async function* f4() { let x: any, y: any; for await (x of y) { } +} +// @filename: file5.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function f5() { + let y: any; + outer: for await (const x of y) { + continue outer; + } +} +// @filename: file6.ts +// https://github.com/Microsoft/TypeScript/issues/21363 +async function* f6() { + let y: any; + outer: for await (const x of y) { + continue outer; + } } \ No newline at end of file From 058e3ad75e5ad5239f3b89c83d3a2dba6933fa24 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 25 Jan 2018 07:36:38 -0800 Subject: [PATCH 096/100] Improve assertion in computePositionOfLineAndCharacter (#21361) --- src/compiler/core.ts | 4 ++++ src/compiler/scanner.ts | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cefa9367b48..06f8b24a4c6 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -340,6 +340,10 @@ namespace ts { return false; } + export function arraysEqual(a: ReadonlyArray, b: ReadonlyArray, equalityComparer: EqualityComparer = equateValues): boolean { + return a.length === b.length && a.every((x, i) => equalityComparer(x, b[i])); + } + export function indexOfAnyCharCode(text: string, charCodes: ReadonlyArray, start?: number): number { for (let i = start || 0; i < text.length; i++) { if (contains(charCodes, text.charCodeAt(i))) { diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 5001ea58336..6b0d10223fd 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -335,7 +335,10 @@ namespace ts { /* @internal */ export function computePositionOfLineAndCharacter(lineStarts: ReadonlyArray, line: number, character: number, debugText?: string): number { - Debug.assert(line >= 0 && line < lineStarts.length); + if (line < 0 || line >= lineStarts.length) { + Debug.fail(`Bad line number. Line: ${line}, lineStarts.length: ${lineStarts.length} , line map is correct? ${debugText !== undefined ? arraysEqual(lineStarts, computeLineStarts(debugText)) : "unknown"}`); + } + const res = lineStarts[line] + character; if (line < lineStarts.length - 1) { Debug.assert(res < lineStarts[line + 1]); From 0b7f6d5911fae749d9dd8e67a0f86dbc2ba8421e Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 25 Jan 2018 07:42:01 -0800 Subject: [PATCH 097/100] Fix bug: Support `this.` completions even when isGlobalCompletion is false (#21330) --- src/services/completions.ts | 5 +++-- tests/cases/fourslash/completionsThisType.ts | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/services/completions.ts b/src/services/completions.ts index 8b7f564ff08..492aca5b26c 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -180,7 +180,7 @@ namespace ts.Completions { let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (kind === CompletionKind.Global && origin && origin.type === "this-type") { + if (origin && origin.type === "this-type") { insertText = needsConvertPropertyAccess ? `this["${name}"]` : `this.${name}`; } else if (needsConvertPropertyAccess) { @@ -683,6 +683,7 @@ namespace ts.Completions { const enum CompletionKind { ObjectPropertyDeclaration, + /** Note that sometimes we access completions from global scope, but use "None" instead of this. See isGlobalCompletionScope. */ Global, PropertyAccess, MemberLike, @@ -2112,13 +2113,13 @@ namespace ts.Completions { const validIdentiferResult: CompletionEntryDisplayNameForSymbol = { name, needsConvertPropertyAccess: false }; if (isIdentifierText(name, target)) return validIdentiferResult; switch (kind) { - case CompletionKind.None: case CompletionKind.MemberLike: return undefined; case CompletionKind.ObjectPropertyDeclaration: // TODO: GH#18169 return { name: JSON.stringify(name), needsConvertPropertyAccess: false }; case CompletionKind.PropertyAccess: + case CompletionKind.None: case CompletionKind.Global: // Don't add a completion for a name starting with a space. See https://github.com/Microsoft/TypeScript/pull/20547 return name.charCodeAt(0) === CharacterCodes.space ? undefined : { name, needsConvertPropertyAccess: true }; diff --git a/tests/cases/fourslash/completionsThisType.ts b/tests/cases/fourslash/completionsThisType.ts index 58325182a22..471251d2134 100644 --- a/tests/cases/fourslash/completionsThisType.ts +++ b/tests/cases/fourslash/completionsThisType.ts @@ -3,7 +3,7 @@ ////class C { //// "foo bar": number; //// xyz() { -//// /**/ +//// return (/**/) //// } ////} //// @@ -11,7 +11,7 @@ goTo.marker(""); -verify.completionListContains("xyz", "(method) C.xyz(): void", "", "method", undefined, undefined, { +verify.completionListContains("xyz", "(method) C.xyz(): any", "", "method", undefined, undefined, { includeInsertTextCompletions: true, insertText: "this.xyz", }); From dcd3b5e1f7582fba9dc4a6c0ac924e836c19444b Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 25 Jan 2018 09:02:20 -0800 Subject: [PATCH 098/100] At `
{ @@ -246,7 +256,7 @@ namespace ts.Completions { const uniques = createMap(); for (const symbol of symbols) { const origin = symbolToOriginInfoMap ? symbolToOriginInfoMap[getSymbolId(symbol)] : undefined; - const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, includeInsertTextCompletions); + const entry = createCompletionEntry(symbol, location, sourceFile, typeChecker, target, kind, origin, recommendedCompletion, propertyAccessToConvert, isJsxInitializer, includeInsertTextCompletions); if (!entry) { continue; } @@ -483,6 +493,7 @@ namespace ts.Completions { location: Node; symbolToOriginInfoMap: SymbolOriginInfoMap; previousToken: Node; + readonly isJsxInitializer: boolean; } function getSymbolCompletionFromEntryId( typeChecker: TypeChecker, @@ -501,7 +512,7 @@ namespace ts.Completions { return { type: "request", request: completionData }; } - const { symbols, location, completionKind, symbolToOriginInfoMap, previousToken } = completionData; + const { symbols, location, completionKind, symbolToOriginInfoMap, previousToken, isJsxInitializer } = completionData; // Find the symbol with the matching entry name. // We don't need to perform character checks here because we're only comparing the @@ -510,7 +521,7 @@ namespace ts.Completions { return firstDefined(symbols, (symbol): SymbolCompletion => { // TODO: Shouldn't need return type annotation (GH#12632) const origin = symbolToOriginInfoMap[getSymbolId(symbol)]; const info = getCompletionEntryDisplayNameForSymbol(symbol, compilerOptions.target, origin, completionKind); - return info && info.name === name && getSourceFromOrigin(origin) === source ? { type: "symbol" as "symbol", symbol, location, symbolToOriginInfoMap, previousToken } : undefined; + return info && info.name === name && getSourceFromOrigin(origin) === source ? { type: "symbol" as "symbol", symbol, location, symbolToOriginInfoMap, previousToken, isJsxInitializer } : undefined; }) || { type: "none" }; } @@ -678,6 +689,7 @@ namespace ts.Completions { readonly symbolToOriginInfoMap: SymbolOriginInfoMap; readonly recommendedCompletion: Symbol | undefined; readonly previousToken: Node | undefined; + readonly isJsxInitializer: boolean; } type Request = { readonly kind: CompletionDataKind.JsDocTagName | CompletionDataKind.JsDocTag } | { readonly kind: CompletionDataKind.JsDocParameterName, tag: JSDocParameterTag }; @@ -859,6 +871,7 @@ namespace ts.Completions { let isRightOfDot = false; let isRightOfOpenTag = false; let isStartingCloseTag = false; + let isJsxInitializer = false; let location = getTouchingPropertyName(sourceFile, position, insideJsDocTagTypeExpression); // TODO: GH#15853 if (contextToken) { @@ -917,6 +930,10 @@ namespace ts.Completions { location = contextToken; } break; + + case SyntaxKind.JsxAttribute: + isJsxInitializer = previousToken.kind === SyntaxKind.EqualsToken; + break; } } } @@ -962,7 +979,7 @@ namespace ts.Completions { log("getCompletionData: Semantic work: " + (timestamp() - semanticStart)); const recommendedCompletion = previousToken && getRecommendedCompletion(previousToken, typeChecker); - return { kind: CompletionDataKind.Data, symbols, completionKind, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken }; + return { kind: CompletionDataKind.Data, symbols, completionKind, propertyAccessToConvert, isNewIdentifierLocation, location, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, previousToken, isJsxInitializer }; type JSDocTagWithTypeExpression = JSDocParameterTag | JSDocPropertyTag | JSDocReturnTag | JSDocTypeTag | JSDocTypedefTag; diff --git a/tests/cases/fourslash/completionsJsxAttributeInitializer.ts b/tests/cases/fourslash/completionsJsxAttributeInitializer.ts new file mode 100644 index 00000000000..510e0630de9 --- /dev/null +++ b/tests/cases/fourslash/completionsJsxAttributeInitializer.ts @@ -0,0 +1,23 @@ +/// + +// @Filename: /a.tsx +////function f(this: { p: number; "a b": number }, x: number): void { +////
; +////} + +goTo.marker(); + +verify.completionListContains("x", "(parameter) x: number", "", "parameter", undefined, undefined, { + includeInsertTextCompletions: true, + insertText: "{x}", +}); + +verify.completionListContains("p", "(property) p: number", "", "property", undefined, undefined, { + includeInsertTextCompletions: true, + insertText: "{this.p}", +}); + +verify.completionListContains("a b", '(property) "a b": number', "", "property", undefined, undefined, { + includeInsertTextCompletions: true, + insertText: '{this["a b"]}', +}); From 8e8a02f8f13dfd0d0da5da703711633b09f2570e Mon Sep 17 00:00:00 2001 From: Sheetal Nandi Date: Thu, 25 Jan 2018 10:06:59 -0800 Subject: [PATCH 099/100] Rename parseConfigFile to getParsedCommandLineOfConfigFile --- src/compiler/watch.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index beccb65f7b8..790295c2254 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -68,7 +68,7 @@ namespace ts { const host: ParseConfigFileHost = system; host.onConfigFileDiagnostic = reportDiagnostic; host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(sys, reportDiagnostic, diagnostic); - const result = parseConfigFile(configFileName, optionsToExtend, host); + const result = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host); host.onConfigFileDiagnostic = undefined; host.onUnRecoverableConfigFileDiagnostic = undefined; return result; @@ -77,7 +77,7 @@ namespace ts { /** * Reads the config file, reports errors if any and exits if the config file cannot be found */ - export function parseConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { + export function getParsedCommandLineOfConfigFile(configFileName: string, optionsToExtend: CompilerOptions, host: ParseConfigFileHost): ParsedCommandLine | undefined { let configFileText: string; try { configFileText = host.readFile(configFileName); @@ -791,7 +791,7 @@ namespace ts { } function parseConfigFile() { - const configParseResult = ts.parseConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost); + const configParseResult = ts.getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost); rootFileNames = configParseResult.fileNames; compilerOptions = configParseResult.options; configFileSpecs = configParseResult.configFileSpecs; From 7c4e755effa696419e39d7f0eeb3bcb51f831e57 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 25 Jan 2018 13:53:08 -0800 Subject: [PATCH 100/100] When applying `// @ts-ignore` fix as a group, apply to a line only once. (#21413) * When applying `// @ts-ignore` fix as a group, apply to a line only once. * Rename line to lineNumber --- .../codefixes/disableJsDiagnostics.ts | 48 ++++++++++--------- .../codeFixDisableJsDiagnosticsInFile_all.ts | 8 ++-- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index a887878bac6..3bcbf886df8 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -1,8 +1,8 @@ /* @internal */ namespace ts.codefix { const fixId = "disableJsDiagnostics"; - const errorCodes = mapDefined(Object.keys(Diagnostics), key => { - const diag = (Diagnostics as MapLike)[key]; + const errorCodes = mapDefined(Object.keys(Diagnostics) as ReadonlyArray, key => { + const diag = Diagnostics[key]; return diag.category === DiagnosticCategory.Error ? diag.code : undefined; }); @@ -19,7 +19,7 @@ namespace ts.codefix { return [{ description: getLocaleSpecificMessage(Diagnostics.Ignore_this_error_message), - changes: [createFileTextChanges(sourceFile.fileName, [getIgnoreCommentLocationForLocation(sourceFile, span.start, newLineCharacter)])], + changes: [createFileTextChanges(sourceFile.fileName, [getIgnoreCommentLocationForLocation(sourceFile, span.start, newLineCharacter).change])], fixId, }, { @@ -35,17 +35,23 @@ namespace ts.codefix { fixId: undefined, }]; }, - fixIds: [fixId], // No point applying as a group, doing it once will fix all errors - getAllCodeActions: context => codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { - if (err.start !== undefined) { - changes.push(getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineOrDefaultFromHost(context.host, context.formatContext.options))); - } - }), + fixIds: [fixId], + getAllCodeActions: context => { + const seenLines = createMap(); // Only need to add `// @ts-ignore` for a line once. + return codeFixAllWithTextChanges(context, errorCodes, (changes, err) => { + if (err.start !== undefined) { + const { lineNumber, change } = getIgnoreCommentLocationForLocation(err.file!, err.start, getNewLineOrDefaultFromHost(context.host, context.formatContext.options)); + if (addToSeen(seenLines, lineNumber)) { + changes.push(change); + } + } + }); + }, }); - function getIgnoreCommentLocationForLocation(sourceFile: SourceFile, position: number, newLineCharacter: string): TextChange { - const { line } = getLineAndCharacterOfPosition(sourceFile, position); - const lineStartPosition = getStartPositionOfLine(line, sourceFile); + function getIgnoreCommentLocationForLocation(sourceFile: SourceFile, position: number, newLineCharacter: string): { lineNumber: number, change: TextChange } { + const { line: lineNumber } = getLineAndCharacterOfPosition(sourceFile, position); + const lineStartPosition = getStartPositionOfLine(lineNumber, sourceFile); const startPosition = getFirstNonSpaceCharacterPosition(sourceFile.text, lineStartPosition); // First try to see if we can put the '// @ts-ignore' on the previous line. @@ -54,19 +60,17 @@ namespace ts.codefix { // if so, we do not want to separate the node from its comment if we can. if (!isInComment(sourceFile, startPosition) && !isInString(sourceFile, startPosition) && !isInTemplateString(sourceFile, startPosition)) { const token = getTouchingToken(sourceFile, startPosition, /*includeJsDocComment*/ false); - const tokenLeadingCommnets = getLeadingCommentRangesOfNode(token, sourceFile); - if (!tokenLeadingCommnets || !tokenLeadingCommnets.length || tokenLeadingCommnets[0].pos >= startPosition) { - return { - span: { start: startPosition, length: 0 }, - newText: `// @ts-ignore${newLineCharacter}` - }; + const tokenLeadingComments = getLeadingCommentRangesOfNode(token, sourceFile); + if (!tokenLeadingComments || !tokenLeadingComments.length || tokenLeadingComments[0].pos >= startPosition) { + return { lineNumber, change: createTextChange(startPosition, 0, `// @ts-ignore${newLineCharacter}`) }; } } // If all fails, add an extra new line immediately before the error span. - return { - span: { start: position, length: 0 }, - newText: `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}` - }; + return { lineNumber, change: createTextChange(position, 0, `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}`) }; + } + + function createTextChange(start: number, length: number, newText: string): TextChange { + return { span: { start, length }, newText }; } } diff --git a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts index 9c3785cf2ae..89f94520374 100644 --- a/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts +++ b/tests/cases/fourslash/codeFixDisableJsDiagnosticsInFile_all.ts @@ -6,15 +6,15 @@ // @Filename: a.js ////let x = ""; -////x = 1; -////x = true; +////x = 1; x = true; +////x = []; verify.codeFixAll({ fixId: "disableJsDiagnostics", newFileContent: `let x = ""; // @ts-ignore -x = 1; +x = 1; x = true; // @ts-ignore -x = true;`, +x = [];`, });