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 2026544a249..9bef7946722 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 + " " + packageJson + " " + versionFile; +task("configure-nightly", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " 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", [configurePrereleaseJs], function () { + var cmd = host + " " + configurePrereleaseJs + " 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"); @@ -1199,23 +1212,12 @@ task("update-sublime", ["local", serverFile], function () { }); var tslintRuleDir = "scripts/tslint/rules"; -var tslintRules = [ - "booleanTriviaRule", - "debugAssertRule", - "nextLineRule", - "noBomRule", - "noDoubleSpaceRule", - "noIncrementDecrementRule", - "noInOperatorRule", - "noTypeAssertionWhitespaceRule", - "objectLiteralSurroundingSpaceRule", - "typeOperatorSpacingRule", -]; +var tslintRules = fs.readdirSync(tslintRuleDir); var tslintRulesFiles = tslintRules.map(function (p) { - return path.join(tslintRuleDir, p + ".ts"); + return path.join(tslintRuleDir, p); }); var tslintRulesOutFiles = tslintRules.map(function (p) { - return path.join(builtLocalDirectory, "tslint/rules", p + ".js"); + return path.join(builtLocalDirectory, "tslint/rules", p.replace(".ts", ".js")); }); var tslintFormattersDir = "scripts/tslint/formatters"; var tslintFormatters = [ 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/scripts/configureNightly.ts b/scripts/configurePrerelease.ts similarity index 79% rename from scripts/configureNightly.ts rename to scripts/configurePrerelease.ts index 778f5ce3727..a63490ca051 100644 --- a/scripts/configureNightly.ts +++ b/scripts/configurePrerelease.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 diff --git a/src/compiler/builder.ts b/src/compiler/builder.ts index 7e44a9608f0..886a2194bd9 100644 --- a/src/compiler/builder.ts +++ b/src/compiler/builder.ts @@ -1,511 +1,581 @@ -/// +/// +/*@internal*/ namespace ts { - export interface EmitOutput { - outputFiles: OutputFile[]; - emitSkipped: boolean; + /** + * 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 + */ + semanticDiagnosticsPerFile: Map> | undefined; + /** + * The map has key by source file's path that has been changed + */ + changedFilesSet: Map; + /** + * Set of affected files being iterated + */ + affectedFiles: ReadonlyArray | undefined; + /** + * Current index to retrieve affected file from + */ + affectedFilesIndex: number | undefined; + /** + * Current changed file for iterating over affected files + */ + 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 + */ + currentAffectedFilesSignatures: Map | undefined; + /** + * Already seen affected files + */ + seenAffectedFiles: Map | undefined; + /** + * program corresponding to this state + */ + program: Program; } - export interface OutputFile { - name: string; - writeByteOrderMark: boolean; - text: string; + 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)); } -} -/* @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 }; + /** + * Create the state so that we can iterate on changedFiles/affected files + */ + 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>(); + } + 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"); + } - function writeFile(fileName: string, text: string, writeByteOrderMark: boolean) { - outputFiles.push({ name: fileName, writeByteOrderMark, text }); + // 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; + } + + /** + * 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(); } } - 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; + /** + * 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++; + } } - interface EmitHandler { - /** - * Called when sourceFile is added to the program - */ - onAddSourceFile(program: Program, sourceFile: SourceFile): void; - /** - * Called when sourceFile is removed from the program - */ - onRemoveSourceFile(path: Path): void; - /** - * 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. - */ - onUpdateSourceFile(program: Program, sourceFile: SourceFile): void; - /** - * 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) - */ - onUpdateSourceFileWithSameVersion(program: Program, sourceFile: SourceFile): boolean; - /** - * Gets the files affected by the script info which has updated shape from the known one - */ - getFilesAffectedByUpdatedShape(program: Program, sourceFile: SourceFile): ReadonlyArray; + /** + * Returns the result with affected file + */ + function toAffectedFileResult(state: BuilderProgramState, result: T, affected: SourceFile | Program): AffectedFileResult { + doneWithAffectedFile(state, affected); + return { result, affected }; } - interface FileInfo { - version: string; - signature: string; + /** + * 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 interface BuilderOptions { - getCanonicalFileName: GetCanonicalFileName; - computeHash: (data: string) => string; + export enum BuilderProgramKind { + SemanticDiagnosticsBuilderProgram, + EmitAndSemanticDiagnosticsBuilderProgram } - export function createBuilder(options: BuilderOptions): Builder { - let isModuleEmit: boolean | undefined; - const fileInfos = createMap(); - 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; - return { - updateProgram, - getFilesAffectedBy, - emitChangedFiles, + 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) { + newProgram = undefined; + oldState = undefined; + return oldProgram; + } + + /** + * Create the canonical file name for identity + */ + const getCanonicalFileName = createGetCanonicalFileName(host.useCaseSensitiveFileNames()); + /** + * Computing hash to for signature verification + */ + const computeHash = host.createHash || identity; + 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: BuilderProgram = { + getState: () => state, + getProgram: () => state.program, + 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, - clear + emit, + getAllDependencies: sourceFile => BuilderState.getAllDependencies(state, state.program, sourceFile), + getCurrentDirectory: () => state.program.getCurrentDirectory() }; - function createProgramGraph(program: Program) { - const currentIsModuleEmit = program.getCompilerOptions().module !== ModuleKind.None; - if (isModuleEmit !== currentIsModuleEmit) { - isModuleEmit = currentIsModuleEmit; - emitHandler = isModuleEmit ? getModuleEmitHandler() : getNonModuleEmitHandler(); - fileInfos.clear(); - semanticDiagnosticsPerFile.clear(); + if (kind === BuilderProgramKind.SemanticDiagnosticsBuilderProgram) { + (result as SemanticDiagnosticsBuilderProgram).getSemanticDiagnosticsOfNextAffectedFile = getSemanticDiagnosticsOfNextAffectedFile; + } + else if (kind === BuilderProgramKind.EmitAndSemanticDiagnosticsBuilderProgram) { + (result as EmitAndSemanticDiagnosticsBuilderProgram).emitNextAffectedFile = emitNextAffectedFile; + } + else { + notImplemented(); + } + + return result; + + /** + * 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 emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult { + const affected = getNextAffectedFile(state, cancellationToken, computeHash); + if (!affected) { + // Done + return undefined; } - 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) - } + + return toAffectedFileResult( + 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 ); } - function registerChangedFile(path: Path) { - changedFilesSet.set(path, true); - // All changed files need to re-evaluate its semantic diagnostics - semanticDiagnosticsPerFile.delete(path); - } + /** + * 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[] = []; - 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); - if (!sourceFile) { - return emptyArray; - } - - if (!updateShapeSignature(program, sourceFile)) { - return [sourceFile]; - } - return emitHandler.getFilesAffectedByUpdatedShape(program, sourceFile); - } - - function emitChangedFiles(program: Program, writeFileCallback: WriteFileCallback): ReadonlyArray { - ensureProgramGraph(program); - const compilerOptions = program.getCompilerOptions(); - - if (!changedFilesSet.size) { - return emptyArray; - } - - // 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)]; - } - - 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); - - if (!seenFiles.has(affectedFile.path)) { - seenFiles.set(affectedFile.path, true); - - // Emit the affected file - (result || (result = [])).push(program.emit(affectedFile, writeFileCallback)); + 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); } - }); - }); - changedFilesSet.clear(); - return result || emptyArray; + return { + emitSkipped, + diagnostics: diagnostics || emptyArray, + emittedFiles, + sourceMaps + }; + } + } + return state.program.emit(targetSourceFile, writeFile || host.writeFile, cancellationToken, emitOnlyDtsFiles, customTransformers); } - function getSemanticDiagnostics(program: Program, cancellationToken?: CancellationToken): ReadonlyArray { - ensureProgramGraph(program); - Debug.assert(changedFilesSet.size === 0); + /** + * 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(cancellationToken?: CancellationToken, ignoreSourceFile?: (sourceFile: SourceFile) => boolean): AffectedFileResult> { + while (true) { + const affected = getNextAffectedFile(state, cancellationToken, computeHash); + if (!affected) { + // Done + return undefined; + } + else if (affected === state.program) { + // When whole program is affected, get all semantic diagnostics (eg when --out or --outFile is specified) + return toAffectedFileResult( + state, + state.program.getSemanticDiagnostics(/*targetSourceFile*/ undefined, cancellationToken), + affected + ); + } - const compilerOptions = program.getCompilerOptions(); + // Get diagnostics for the affected file if its not ignored + if (ignoreSourceFile && ignoreSourceFile(affected as SourceFile)) { + // Get next affected file + doneWithAffectedFile(state, affected); + continue; + } + + return toAffectedFileResult( + state, + getSemanticDiagnosticsOfFile(state, affected as SourceFile, cancellationToken), + affected + ); + } + } + + /** + * 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 + */ + function getSemanticDiagnostics(sourceFile?: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { + assertSourceFileOkWithoutNextAffectedCall(state, sourceFile); + const compilerOptions = state.program.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 program.getSemanticDiagnostics(/*sourceFile*/ undefined, cancellationToken); + return state.program.getSemanticDiagnostics(sourceFile, cancellationToken); + } + + if (sourceFile) { + 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 program.getSourceFiles()) { - diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(program, sourceFile, cancellationToken)); + for (const sourceFile of state.program.getSourceFiles()) { + diagnostics = addRange(diagnostics, getSemanticDiagnosticsOfFile(state, sourceFile, cancellationToken)); } return diagnostics || emptyArray; } + } +} - function getSemanticDiagnosticsOfFile(program: Program, sourceFile: SourceFile, cancellationToken?: CancellationToken): ReadonlyArray { - const path = sourceFile.path; - const cachedDiagnostics = semanticDiagnosticsPerFile.get(path); - // Report the semantic diagnostics from the cache if we already have those diagnostics present - if (cachedDiagnostics) { - return cachedDiagnostics; - } +namespace ts { + export type AffectedFileResult = { result: T; affected: SourceFile | Program; } | undefined; - // Diagnostics werent cached, get them from program, and cache the result - const diagnostics = program.getSemanticDiagnostics(sourceFile, cancellationToken); - semanticDiagnosticsPerFile.set(path, diagnostics); - return diagnostics; - } - - function clear() { - isModuleEmit = undefined; - emitHandler = undefined; - fileInfos.clear(); - semanticDiagnosticsPerFile.clear(); - changedFilesSet.clear(); - hasShapeChanged.clear(); - } - + export interface BuilderProgramHost { + /** + * 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; + /** + * 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 BuilderProgram { + /*@internal*/ + getState(): BuilderProgramState; + /** + * Returns current program + */ + getProgram(): Program; + /** + * Get compiler options of the program + */ + 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(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; /** - * 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. + * 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 containsOnlyAmbientModules(sourceFile: SourceFile) { - for (const statement of sourceFile.statements) { - if (!isModuleWithStringLiteralName(statement)) { - return false; - } - } - return true; - } - + emit(targetSourceFile?: SourceFile, writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): EmitResult; /** - * @return {boolean} indicates if the shape signature has changed since last update. + * Get the current directory of the program */ - function updateShapeSignature(program: Program, sourceFile: SourceFile) { - 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)) { - return false; - } + getCurrentDirectory(): string; + } - hasShapeChanged.set(sourceFile.path, true); - const info = fileInfos.get(sourceFile.path); - Debug.assert(!!info); - - const prevSignature = info.signature; - let latestSignature: string; - if (sourceFile.isDeclarationFile) { - latestSignature = sourceFile.version; - info.signature = latestSignature; - } - 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; - } - else { - latestSignature = prevSignature; - } - } - - return !prevSignature || latestSignature !== prevSignature; - } - + /** + * The builder that caches the semantic diagnostics for the program and handles the changed files and affected files + */ + export interface SemanticDiagnosticsBuilderProgram extends BuilderProgram { /** - * Gets the referenced files for a file from the program with values for the keys as referenced file's path to be true + * Gets the semantic diagnostics from the program for the next affected file and caches it + * Returns undefined if the iteration is complete */ - 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, options.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, options.getCanonicalFileName); - addReferencedFile(typeFilePath); - }); - } - - return referencedFiles; - - function addReferencedFile(referencedPath: Path) { - if (!referencedFiles) { - referencedFiles = createMap(); - } - referencedFiles.set(referencedPath, true); - } - } - + 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 EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { /** - * Gets all files of the program excluding the default library file + * 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 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); - } - } - } - - 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); - } - } - - 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); - } - } - - function updateReferences(program: Program, sourceFile: SourceFile) { - const newReferences = getReferencedFiles(program, sourceFile); - if (newReferences) { - references.set(sourceFile.path, newReferences); - } - else { - references.delete(sourceFile.path); - } - } - - function updateReferencesTrackingChangedReferences(program: Program, sourceFile: SourceFile) { - const newReferences = getReferencedFiles(program, sourceFile); - if (!newReferences) { - // Changed if we had references - return references.delete(sourceFile.path); - } - - 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 arrayFrom(mapDefinedIterator(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)); - } - } - } - - // Return array of values that needs emit - return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value)); - } - } + emitNextAffectedFile(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult; + } + + /** + * Create the builder to manage semantic diagnostics and cache them + */ + 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; + 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/builderState.ts b/src/compiler/builderState.ts new file mode 100644 index 00000000000..581aa05f12c --- /dev/null +++ b/src/compiler/builderState.ts @@ -0,0 +1,384 @@ +/// +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 }); + } + } + + 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 + */ + export interface FileInfo { + readonly version: string; + signature: string | undefined; + } + /** + * Referenced files with values for the keys as referenced file's path to be true + */ + export type ReferencedSet = ReadonlyMap; + /** + * Compute the hash to store the shape of the file + */ + 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 + */ + 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); + } + } + + /** + * 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; + } + + /** + * 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 = canReuseOldState(referencedMap, oldState); + + // 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 }); + } + + return { + fileInfos, + referencedMap, + hasCalledUpdateShapeSignature, + allFilesExcludingDefaultLibraryFile: undefined, + allFileNames: undefined + }; + } + + /** + * 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) { + 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(emitOutput.outputFiles[0].text); + } + else { + latestSignature = prevSignature; + } + } + cacheToUpdateSignature.set(sourceFile.path, latestSignature); + + 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 arrayFrom(mapDefinedIterator(seenMap.keys(), path => { + const file = programOfThisState.getSourceFileByPath(path as Path); + return file ? file.fileName : 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 + */ + function getReferencedByPaths(state: Readonly, referencedFilePath: Path) { + return arrayFrom(mapDefinedIterator(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 array of values that needs emit + return arrayFrom(mapDefinedIterator(seenFileNamesMap.values(), value => value)); + } +} diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3e8d5b86af7..26f0baec5ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -749,10 +749,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; } @@ -2422,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; } @@ -2439,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]; } @@ -2467,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); } @@ -3949,7 +3954,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); @@ -5992,7 +5998,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); } } } @@ -6174,7 +6182,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; @@ -6562,7 +6570,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; @@ -9635,7 +9643,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)) { @@ -9671,7 +9679,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)) { @@ -9679,10 +9687,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; } @@ -10963,7 +10972,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 => { @@ -10972,7 +10981,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; @@ -11027,7 +11036,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) { @@ -11763,16 +11774,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); } @@ -12886,19 +12887,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); } @@ -12988,7 +12990,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) { @@ -13047,7 +13049,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); @@ -15309,7 +15311,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 { @@ -15554,7 +15556,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, @@ -16716,7 +16718,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; @@ -16845,7 +16847,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; @@ -21029,6 +21031,7 @@ namespace ts { } break; case SyntaxKind.IndexSignature: + case SyntaxKind.SemicolonClassElement: // Can't be private break; default: diff --git a/src/compiler/core.ts b/src/compiler/core.ts index afdc65de51d..2966582cb39 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`; } @@ -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 */ @@ -183,6 +187,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) { @@ -336,6 +344,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))) { @@ -1457,6 +1469,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"); @@ -1890,10 +1905,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, "/"); } @@ -2927,9 +2938,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; } /** @@ -3054,223 +3063,10 @@ 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 const emptyFileSystemEntries: FileSystemEntries = { files: emptyArray, directories: emptyArray }; - export function createCachedDirectoryStructureHost(host: DirectoryStructureHost): CachedDirectoryStructureHost { - const cachedReadDirectoryResult = createMap(); - const getCurrentDirectory = memoize(() => host.getCurrentDirectory()); - 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, - 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 tryReadDirectory(dir, path) || emptyFileSystemEntries; - } - } - - 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 singleElementArray(t: T | undefined): T[] | undefined { return t === undefined ? undefined : [t]; diff --git a/src/compiler/declarationEmitter.ts b/src/compiler/declarationEmitter.ts index aa1ec82a631..1a35f520ce5 100644 --- a/src/compiler/declarationEmitter.ts +++ b/src/compiler/declarationEmitter.ts @@ -2000,7 +2000,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/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/compiler/program.ts b/src/compiler/program.ts index 37fae7195ab..bd45857fc51 100755 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,7 +1,6 @@ /// /// /// -/// namespace ts { const ignoreDiagnosticCommentRegEx = /(^\s*$)|(^\s*\/\/\/?\s*(@ts-ignore)?)/; @@ -1141,32 +1140,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 + }; + } } } @@ -1178,7 +1179,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, emitOnlyDtsFiles); performance.mark("beforeEmit"); diff --git a/src/compiler/resolutionCache.ts b/src/compiler/resolutionCache.ts index aac25c5d596..e907d2d62b7 100644 --- a/src/compiler/resolutionCache.ts +++ b/src/compiler/resolutionCache.ts @@ -9,12 +9,12 @@ 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; removeResolutionsOfFile(filePath: Path): void; - createHasInvalidatedResolution(): HasInvalidatedResolution; + createHasInvalidatedResolution(forceAllFilesAsInvalidated?: boolean): HasInvalidatedResolution; startCachingPerDirectoryResolution(): void; finishCachingPerDirectoryResolution(): void; @@ -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; @@ -73,7 +73,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; @@ -88,6 +88,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, @@ -159,8 +160,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; @@ -307,12 +308,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 ); } @@ -468,14 +469,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); - } - - // Ignore emits from the program - if (isEmittedFileOfProgram(resolutionHost.getCurrentProgram(), fileOrDirectory)) { - return; + cachedDirectoryStructureHost.addOrDeleteFileOrDirectory(fileOrDirectory, fileOrDirectoryPath); } // If the files are added to project root or node_modules directory, always run through the invalidation process @@ -581,6 +577,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; } @@ -602,9 +602,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/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]); diff --git a/src/compiler/sys.ts b/src/compiler/sys.ts index d5eeb6a58cc..3256eef4dc2 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 { + export interface System { + args: string[]; 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; - 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 { - args: string[]; 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/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/src/compiler/tsc.ts b/src/compiler/tsc.ts index 01fb45e4f7d..15e4867d7f7 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(sys); 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 = parseConfigFileWithSystem(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); @@ -145,44 +144,42 @@ 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 createWatchingSystemHost(reportWatchDiagnostic?: DiagnosticReporter) { - 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); - 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()); }; - return watchingHost; } - function compileProgram(program: Program): ExitStatus { - let diagnostics: Diagnostic[]; + function createWatchStatusReporter(options: CompilerOptions) { + return ts.createWatchStatusReporter(sys, !!options.pretty); + } - // First get and report any syntactic errors. - diagnostics = program.getSyntacticDiagnostics().slice(); + function createWatchOfConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions) { + 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; + watchCompilerHost.configFileSpecs = configParseResult.configFileSpecs; + watchCompilerHost.configFileWildCardDirectories = configParseResult.wildcardDirectories; + createWatchProgram(watchCompilerHost); + } - // 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); - - return handleEmitOutputAndReportErrors(sys, program, emittedFiles, emitSkipped, diagnostics, reportDiagnostic); + function createWatchOfFilesAndCompilerOptions(rootFiles: string[], options: CompilerOptions) { + const watchCompilerHost = ts.createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, sys, /*createProgram*/ undefined, reportDiagnostic, createWatchStatusReporter(options)); + updateWatchCompilationHost(watchCompilerHost); + createWatchProgram(watchCompilerHost); } function enableStatistics(compilerOptions: CompilerOptions) { 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/types.ts b/src/compiler/types.ts index 703b04699b8..f6ca2f45d34 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2892,7 +2892,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; @@ -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/utilities.ts b/src/compiler/utilities.ts index 4189ef89e75..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); @@ -3380,14 +3385,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; } /** @@ -4704,7 +4709,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 { diff --git a/src/compiler/watch.ts b/src/compiler/watch.ts index 79e26f5986c..790295c2254 100644 --- a/src/compiler/watch.ts +++ b/src/compiler/watch.ts @@ -2,128 +2,161 @@ /// /// -/* @internal */ +/*@internal*/ 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, builder: Builder): void; - - // Only for testing - maxNumberOfFilesToIterateForInvalidation?: number; - } - - 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 + */ + export function createDiagnosticReporter(system: System, 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; }; } - /** @internal */ - export function createWatchDiagnosticReporterWithColor(system = sys): DiagnosticReporter { - return diagnostic => { + function clearScreenIfNotWatchingForFileChanges(system: System, diagnostic: Diagnostic) { + if (system.clearScreen && diagnostic.code !== Diagnostics.Compilation_complete_Watching_for_file_changes.code) { + system.clearScreen(); + } + } + + /** + * Create a function that reports watch status by writing to the system and handles the formating of the diagnostic + */ + export function createWatchStatusReporter(system: System, pretty?: boolean): WatchStatusReporter { + return pretty ? + (diagnostic: Diagnostic, newLine: string) => { + clearScreenIfNotWatchingForFileChanges(system, diagnostic); let output = `[${ formatColorAndReset(new Date().toLocaleTimeString(), ForegroundColorEscapeSequences.Grey) }] `; - output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${system.newLine + system.newLine + system.newLine}`; + output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine + newLine}`; + system.write(output); + } : + (diagnostic: Diagnostic, newLine: string) => { + clearScreenIfNotWatchingForFileChanges(system, diagnostic); + let output = new Date().toLocaleTimeString() + " - "; + output += `${flattenDiagnosticMessageText(diagnostic.messageText, system.newLine)}${newLine + newLine + newLine}`; system.write(output); }; } - export function reportDiagnostics(diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter): void { - for (const diagnostic of diagnostics) { - reportDiagnostic(diagnostic); - } + /** + * Interface extending ParseConfigHost to support ParseConfigFile that reads config file and reports errors + */ + export interface ParseConfigFileHost extends ParseConfigHost, ConfigFileDiagnosticsReporter { + getCurrentDirectory(): string; } - export function reportDiagnosticSimply(diagnostic: Diagnostic, host: FormatDiagnosticsHost, system: System): void { - system.write(ts.formatDiagnostic(diagnostic, host)); + /** Parses config file using System interface */ + 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 = getParsedCommandLineOfConfigFile(configFileName, optionsToExtend, host); + host.onConfigFileDiagnostic = undefined; + host.onUnRecoverableConfigFileDiagnostic = undefined; + return result; } - 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 + */ + export function getParsedCommandLineOfConfigFile(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); - reportWatchDiagnostic(error); - system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; } if (!configFileText) { const error = createCompilerDiagnostic(Diagnostics.File_0_not_found, configFileName); - reportDiagnostics([error], reportDiagnostic); - system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); - return; + host.onUnRecoverableConfigFileDiagnostic(error); + return undefined; } const result = parseJsonText(configFileName, configFileText); - reportDiagnostics(result.parseDiagnostics, 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)); - reportDiagnostics(configParseResult.errors, 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; } - function reportEmittedFiles(files: string[], system: DirectoryStructureHost): void { - if (!files || files.length === 0) { - return; - } - const currentDir = system.getCurrentDirectory(); - for (const file of files) { - const filepath = getNormalizedAbsolutePath(file, currentDir); - system.write(`TSFILE: ${filepath}${system.newLine}`); - } + /** + * 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; } - export function handleEmitOutputAndReportErrors(system: DirectoryStructureHost, program: Program, - emittedFiles: string[], emitSkipped: boolean, - diagnostics: Diagnostic[], reportDiagnostic: DiagnosticReporter - ): ExitStatus { - reportDiagnostics(sortAndDeduplicateDiagnostics(diagnostics), reportDiagnostic); - reportEmittedFiles(emittedFiles, system); + /** + * 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 (program.getCompilerOptions().listFiles) { - forEach(program.getSourceFiles(), file => { - system.write(file.fileName + system.newLine); + // 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) { @@ -138,110 +171,268 @@ namespace ts { 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 || pretty ? createWatchDiagnosticReporterWithColor(system) : createWatchDiagnosticReporter(system); - parseConfigFile = parseConfigFile || ts.parseConfigFile; + 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, 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); return { - system, - parseConfigFile, - reportDiagnostic, - reportWatchDiagnostic, - beforeCompile: noop, - afterCompile: compileWatchedProgram, + useCaseSensitiveFileNames, + getNewLine: () => system.newLine, + getCurrentDirectory: () => system.getCurrentDirectory(), + getDefaultLibLocation, + getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), + 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)), + getEnvironmentVariable: system.getEnvironmentVariable && (name => system.getEnvironmentVariable(name)), + 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: reportWatchStatus || createWatchStatusReporter(system), + createDirectory: path => system.createDirectory(path), + writeFile: (path, data, writeByteOrderMark) => system.writeFile(path, data, writeByteOrderMark), + onCachedDirectoryStructureHostCreate: cacheHost => host = cacheHost || system, + createHash: system.createHash && (s => system.createHash(s)), + createProgram, + afterProgramCreate: emitFilesAndReportErrorUsingBuilder }; - function compileWatchedProgram(host: DirectoryStructureHost, program: Program, builder: Builder) { - // First get and report any syntactic errors. - const diagnostics = program.getSyntacticDiagnostics().slice(); - let reportSemanticDiagnostics = false; + function getDefaultLibLocation() { + return getDirectoryPath(normalizePath(system.getExecutingFilePath())); + } - // 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; - - 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); - } - } - - if (reportSemanticDiagnostics) { - addRange(diagnostics, builder.getSemanticDiagnostics(program)); - } - return handleEmitOutputAndReportErrors(host, program, emittedFiles, emitSkipped, - diagnostics, reportDiagnostic); - - 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); - } - } - } + function emitFilesAndReportErrorUsingBuilder(builderProgram: BuilderProgram) { + emitFilesAndReportErrors(builderProgram, reportDiagnostic, writeFileName); } } - export function createWatchModeWithConfigFile(configParseResult: ParsedCommandLine, optionsToExtend: CompilerOptions = {}, watchingHost?: WatchingSystemHost) { - return createWatchMode(configParseResult.fileNames, configParseResult.options, watchingHost, configParseResult.options.configFilePath, configParseResult.configFileSpecs, configParseResult.wildcardDirectories, optionsToExtend); + /** + * Report error and exit + */ + function reportUnrecoverableDiagnostic(system: System, reportDiagnostic: DiagnosticReporter, diagnostic: Diagnostic) { + reportDiagnostic(diagnostic); + system.exit(ExitStatus.DiagnosticsPresent_OutputsSkipped); } - export function createWatchModeWithoutConfigFile(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost) { - return createWatchMode(rootFileNames, compilerOptions, watchingHost); + /** + * Creates the watch compiler host from system for config file in watch mode + */ + 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, createProgram, reportDiagnostic, reportWatchStatus) as WatchCompilerHostOfConfigFile; + host.onConfigFileDiagnostic = reportDiagnostic; + host.onUnRecoverableConfigFileDiagnostic = diagnostic => reportUnrecoverableDiagnostic(system, reportDiagnostic, diagnostic); + host.configFileName = configFileName; + host.optionsToExtend = optionsToExtend; + return host; } - interface HostFileInfo { - version: number; - sourceFile: SourceFile; - fileWatcher: FileWatcher; + /** + * 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, 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; + } +} + +namespace ts { + export type DiagnosticReporter = (diagnostic: Diagnostic) => void; + export type WatchStatusReporter = (diagnostic: Diagnostic, newLine: string) => 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: T): void; + /** If provided, called with Diagnostic message that informs about change in watch status */ + onWatchStatusChange?(diagnostic: Diagnostic, newLine: string): void; + + // Only for testing + /*@internal*/ + maxNumberOfFilesToIterateForInvalidation?: number; + + // Sub set of compiler host methods to read and generate new program + useCaseSensitiveFileNames(): boolean; + getNewLine(): string; + 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 + */ + 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 would be used to write log about compilation */ + trace?(s: string): void; + /** If provided is used to get the environment variable */ + getEnvironmentVariable?(name: string): string; + + /** 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 | undefined)[]; + + /** 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; + /** 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; } - function createWatchMode(rootFileNames: string[], compilerOptions: CompilerOptions, watchingHost?: WatchingSystemHost, configFileName?: string, configFileSpecs?: ConfigFileSpecs, configFileWildCardDirectories?: MapLike, optionsToExtendForConfigFile?: CompilerOptions) { - let program: Program; + /** 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 + */ + export interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { + /** root files to use to generate program */ + rootFiles: string[]; + + /** Compiler options */ + options: CompilerOptions; + } + + /** + * Reports config file diagnostics + */ + export interface ConfigFileDiagnosticsReporter { + /** + * Reports the diagnostics in reading/writing or parsing of the config file + */ + onConfigFileDiagnostic: DiagnosticReporter; + + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + + /** + * Host to create watch with config file + */ + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { + /** Name of the config file to compile */ + configFileName: string; + + /** Options to extend */ + optionsToExtend?: CompilerOptions; + + /** + * 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[]; + } + + /** + * Host to create watch with config file that is already parsed (from tsc) + */ + /*@internal*/ + export interface WatchCompilerHostOfConfigFile extends WatchCompilerHost { + rootFiles?: string[]; + options?: CompilerOptions; + optionsToExtend?: CompilerOptions; + configFileSpecs?: ConfigFileSpecs; + configFileWildCardDirectories?: MapLike; + } + + export interface Watch { + /** Synchronize with host and get updated program */ + getProgram(): T; + /** Gets the existing program without synchronizing with changes on host */ + /*@internal*/ + getCurrentProgram(): T; + } + + /** + * 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 watch compiler host for either configFile or fileNames and its options + */ + 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, createProgram, reportDiagnostic, reportWatchStatus); + } + else { + 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; + /** + * Creates the watch from the host for config file + */ + export function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; + export function createWatchProgram(host: WatchCompilerHostOfFilesAndCompilerOptions & WatchCompilerHostOfConfigFile): WatchOfFilesAndCompilerOptions | WatchOfConfigFile { + interface HostFileInfo { + version: number; + sourceFile: SourceFile; + fileWatcher: FileWatcher; + } + + 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 @@ -252,112 +443,143 @@ 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 loggingEnabled = compilerOptions.diagnostics || compilerOptions.extendedDiagnostics; - const writeLog: (s: string) => void = loggingEnabled ? s => { system.write(s); system.write(system.newLine); } : noop; + const useCaseSensitiveFileNames = host.useCaseSensitiveFileNames(); + 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 = {}, createProgram } = host; + 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: (path, extensions, exclude, include, depth) => directoryStructureHost.readDirectory(path, extensions, exclude, include, depth), + fileExists: path => host.fileExists(path), + readFile, + getCurrentDirectory, + onConfigFileDiagnostic: host.onConfigFileDiagnostic, + onUnRecoverableConfigFileDiagnostic: host.onUnRecoverableConfigFileDiagnostic + }; + + // From tsc we want to get already parsed result and hence check for rootFileNames + if (configFileName && !rootFileNames) { + parseConfigFile(); + } + + 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; - 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); + watchFile(host, configFileName, scheduleProgramReload, writeLog); } - 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 getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames); + let newLine = updateNewLine(); const compilerHost: CompilerHost & ResolutionCacheHost = { // Members for CompilerHost getSourceFile: (fileName, languageVersion, onError?, shouldCreateNewSourceFile?) => getVersionedSourceFileByPath(fileName, toPath(fileName), languageVersion, onError, shouldCreateNewSourceFile), getSourceFileByPath: getVersionedSourceFileByPath, - getDefaultLibLocation, - getDefaultLibFileName: options => combinePaths(getDefaultLibLocation(), getDefaultLibFileName(options)), - writeFile: notImplemented, + getDefaultLibLocation: host.getDefaultLibLocation && (() => host.getDefaultLibLocation()), + getDefaultLibFileName: options => host.getDefaultLibFileName(options), + writeFile, getCurrentDirectory, - useCaseSensitiveFileNames: () => system.useCaseSensitiveFileNames, + useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, getCanonicalFileName, getNewLine: () => newLine, fileExists, - readFile: fileName => system.readFile(fileName), - trace: s => system.write(s + newLine), - directoryExists: directoryName => directoryStructureHost.directoryExists(directoryName), - getEnvironmentVariable: name => system.getEnvironmentVariable ? system.getEnvironmentVariable(name) : "", - getDirectories: path => directoryStructureHost.getDirectories(path), - realpath, - resolveTypeReferenceDirectives: (typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile), - resolveModuleNames: (moduleNames, containingFile, reusedNames?) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames, /*logChanges*/ false), + readFile, + trace, + directoryExists: directoryStructureHost.directoryExists && (path => directoryStructureHost.directoryExists(path)), + getDirectories: directoryStructureHost.getDirectories && (path => directoryStructureHost.getDirectories(path)), + 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, watchDirectoryOfFailedLookupLocation: watchDirectory, watchTypeRootsDirectory: watchDirectory, - getCachedDirectoryStructureHost, + getCachedDirectoryStructureHost: () => cachedDirectoryStructureHost, onInvalidatedResolution: scheduleProgramUpdate, onChangedAutomaticTypeDirectiveNames: () => { hasChangedAutomaticTypeDirectiveNames = true; scheduleProgramUpdate(); }, - maxNumberOfFilesToIterateForInvalidation: watchingHost.maxNumberOfFilesToIterateForInvalidation, + maxNumberOfFilesToIterateForInvalidation: host.maxNumberOfFilesToIterateForInvalidation, getCurrentProgram, writeLog }; // Cache for the module resolution const resolutionCache = createResolutionCache(compilerHost, configFileName ? - getDirectoryPath(getNormalizedAbsolutePath(configFileName, getCurrentDirectory())) : - getCurrentDirectory() + getDirectoryPath(getNormalizedAbsolutePath(configFileName, currentDirectory)) : + currentDirectory, + /*logChangesWhenResolvingModule*/ false ); - // There is no extra check needed since we can just rely on the program to decide emit - const builder = createBuilder({ getCanonicalFileName, computeHash }); + // Resolve module using host module resolution strategy if provided otherwise use resolution cache to resolve module names + compilerHost.resolveModuleNames = host.resolveModuleNames ? + ((moduleNames, containingFile, reusedNames) => host.resolveModuleNames(moduleNames, containingFile, reusedNames)) : + ((moduleNames, containingFile, reusedNames) => resolutionCache.resolveModuleNames(moduleNames, containingFile, reusedNames)); + compilerHost.resolveTypeReferenceDirectives = host.resolveTypeReferenceDirectives ? + ((typeDirectiveNames, containingFile) => host.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile)) : + ((typeDirectiveNames, containingFile) => resolutionCache.resolveTypeReferenceDirectives(typeDirectiveNames, containingFile)); + const userProvidedResolution = !!host.resolveModuleNames || !!host.resolveTypeReferenceDirectives; - clearHostScreen(); - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Starting_compilation_in_watch_mode)); + reportWatchDiagnostic(Diagnostics.Starting_compilation_in_watch_mode); synchronizeProgram(); // Update the wild card directory watch watchConfigFileWildCardDirectories(); - return getCurrentProgram; + return configFileName ? + { getCurrentProgram: getCurrentBuilderProgram, getProgram: synchronizeProgram } : + { getCurrentProgram: getCurrentBuilderProgram, getProgram: synchronizeProgram, updateRootFileNames }; + + function getCurrentBuilderProgram() { + return builderProgram; + } function getCurrentProgram() { - return program; + return builderProgram && builderProgram.getProgram(); } function synchronizeProgram() { writeLog(`Synchronizing program`); + const program = getCurrentProgram(); if (hasChangedCompilerOptions) { - newLine = getNewLineCharacter(compilerOptions, system); + newLine = updateNewLine(); if (program && changesAffectModuleResolution(program.getCompilerOptions(), compilerOptions)) { resolutionCache.clear(); } } - const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(); - if (isProgramUptoDate(program, rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { - return; + // All resolutions are invalid if user provided resolutions + const hasInvalidatedResolution = resolutionCache.createHasInvalidatedResolution(userProvidedResolution); + if (isProgramUptoDate(getCurrentProgram(), rootFileNames, compilerOptions, getSourceVersion, fileExists, hasInvalidatedResolution, hasChangedAutomaticTypeDirectiveNames)) { + return builderProgram; } - beforeCompile(compilerOptions); - // Compile the program const needsUpdateInTypeRootWatch = hasChangedCompilerOptions || !program; hasChangedCompilerOptions = false; resolutionCache.startCachingPerDirectoryResolution(); compilerHost.hasInvalidatedResolution = hasInvalidatedResolution; compilerHost.hasChangedAutomaticTypeDirectiveNames = hasChangedAutomaticTypeDirectiveNames; - program = createProgram(rootFileNames, compilerOptions, compilerHost, program); + builderProgram = createProgram(rootFileNames, compilerOptions, compilerHost, builderProgram); resolutionCache.finishCachingPerDirectoryResolution(); - builder.updateProgram(program); // Update watches - updateMissingFilePathsWatch(program, missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath); + updateMissingFilePathsWatch(builderProgram.getProgram(), missingFilesMap || (missingFilesMap = createMap()), watchMissingFilePath); if (needsUpdateInTypeRootWatch) { resolutionCache.updateTypeRootsWatch(); } @@ -376,12 +598,25 @@ namespace ts { missingFilePathsRequestedForRelease = undefined; } - afterCompile(directoryStructureHost, program, builder); - reportWatchDiagnostic(createCompilerDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes)); + if (host.afterProgramCreate) { + host.afterProgramCreate(builderProgram); + } + reportWatchDiagnostic(Diagnostics.Compilation_complete_Watching_for_file_changes); + return builderProgram; + } + + function updateRootFileNames(files: string[]) { + Debug.assert(!configFileName, "Cannot update root file names with config file watch mode"); + rootFileNames = files; + scheduleProgramUpdate(); + } + + function updateNewLine() { + return getNewLineCharacter(compilerOptions, () => host.getNewLine()); } function toPath(fileName: string) { - return ts.toPath(fileName, getCurrentDirectory(), getCanonicalFileName); + return ts.toPath(fileName, currentDirectory, getCanonicalFileName); } function fileExists(fileName: string) { @@ -394,10 +629,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 @@ -416,7 +647,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 { @@ -428,9 +659,9 @@ namespace ts { else { let fileWatcher: FileWatcher; if (sourceFile) { - sourceFile.version = "0"; - fileWatcher = watchFilePath(system, fileName, onSourceFileChange, path, writeLog); - sourceFilesCache.set(path, { sourceFile, version: 0, fileWatcher }); + sourceFile.version = "1"; + fileWatcher = watchFilePath(host, fileName, onSourceFileChange, path, writeLog); + sourceFilesCache.set(path, { sourceFile, version: 1, fileWatcher }); } else { sourceFilesCache.set(path, "0"); @@ -444,7 +675,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"); } @@ -492,18 +723,24 @@ namespace ts { } } + 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() { @@ -512,17 +749,9 @@ namespace ts { scheduleProgramUpdate(); } - function clearHostScreen() { - if (watchingHost.system.clearScreen) { - watchingHost.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: @@ -530,14 +759,15 @@ namespace ts { case ConfigFileProgramReloadLevel.Full: return reloadConfigFile(); default: - return synchronizeProgram(); + synchronizeProgram(); + return; } } 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) { - reportDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); + host.onConfigFileDiagnostic(getErrorForNoInputFiles(configFileSpecs, configFileName)); } rootFileNames = result.fileNames; @@ -549,21 +779,25 @@ namespace ts { writeLog(`Reloading config file: ${configFileName}`); reloadLevel = ConfigFileProgramReloadLevel.None; - const cachedHost = directoryStructureHost as CachedDirectoryStructureHost; - cachedHost.clearCache(); - const configParseResult = parseConfigFile(configFileName, optionsToExtendForConfigFile, cachedHost, reportDiagnostic, reportWatchDiagnostic); - rootFileNames = configParseResult.fileNames; - compilerOptions = configParseResult.options; + if (cachedDirectoryStructureHost) { + cachedDirectoryStructureHost.clearCache(); + } + parseConfigFile(); hasChangedCompilerOptions = true; - configFileSpecs = configParseResult.configFileSpecs; - configFileWildCardDirectories = configParseResult.wildcardDirectories; - synchronizeProgram(); // Update the wild card directory watch watchConfigFileWildCardDirectories(); } + function parseConfigFile() { + const configParseResult = ts.getParsedCommandLineOfConfigFile(configFileName, optionsToExtendForConfigFile, parseConfigFileHost); + 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); @@ -573,7 +807,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 { @@ -593,17 +827,17 @@ 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); } } 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) { @@ -622,11 +856,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) { @@ -638,7 +877,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 @@ -669,8 +908,29 @@ namespace ts { ); } - function computeHash(data: string) { - return system.createHash ? system.createHash(data) : data; + 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/compiler/watchUtilities.ts b/src/compiler/watchUtilities.ts index 0bc6920479b..f5c614d9d57 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: (path, encoding) => host.readFile(path, encoding), + directoryExists: host.directoryExists && directoryExists, + getDirectories, + readDirectory, + createDirectory: host.createDirectory && createDirectory, + writeFile: host.writeFile && 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 tryReadDirectory(dir, path) || emptyFileSystemEntries; + } + } + + 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 */ @@ -90,53 +331,61 @@ namespace ts { return program.isEmittedFile(file); } - 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/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] : ""; diff --git a/src/harness/fourslash.ts b/src/harness/fourslash.ts index df870034270..98e8e61eef5 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 { @@ -1102,25 +1087,35 @@ 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(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)) { 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 }; - if (isInString !== undefined) { - result.isInString = isInString; - } - return result; - } } public verifyNoReferences(markerNameOrRange?: string | Range) { @@ -1139,7 +1134,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 }]); } @@ -1305,8 +1300,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[] }) { @@ -2568,18 +2568,14 @@ 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); } - 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)}`); } @@ -4077,7 +4073,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); } @@ -4589,10 +4585,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/harness/unittests/builder.ts b/src/harness/unittests/builder.ts index a5d880fa551..8808a151c04 100644 --- a/src/harness/unittests/builder.ts +++ b/src/harness/unittests/builder.ts @@ -41,22 +41,97 @@ 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"]); + + // 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"]); + }); }); function makeAssertChanges(getProgram: () => Program): (fileNames: ReadonlyArray) => void { - const builder = createBuilder({ - getCanonicalFileName: identity, - computeHash: identity - }); + 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[] = []; - builder.emitChangedFiles(program, fileName => outputFileNames.push(fileName)); + // tslint:disable-next-line no-empty + while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName))) { + } assert.deepEqual(outputFileNames, fileNames); }; } + function makeAssertChangesWithCancellationToken(getProgram: () => Program): (fileNames: ReadonlyArray, cancelAfterEmitLength?: number) => void { + const host: BuilderProgramHost = { useCaseSensitiveFileNames: returnTrue }; + let builderProgram: EmitAndSemanticDiagnosticsBuilderProgram | undefined; + 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(); + builderProgram = createEmitAndSemanticDiagnosticsBuilderProgram(program, host, builderProgram); + const outputFileNames: string[] = []; + try { + // tslint:disable-next-line no-empty + do { + assert.isFalse(cancel); + if (outputFileNames.length === cancelAfterEmitLength) { + cancel = true; + } + } while (builderProgram.emitNextAffectedFile(fileName => outputFileNames.push(fileName), cancellationToken)); + } + catch (e) { + assert.isFalse(operationWasCancelled); + assert(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/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/harness/unittests/extractRanges.ts b/src/harness/unittests/extractRanges.ts index 493c9639c3d..00fbf334b38 100644 --- a/src/harness/unittests/extractRanges.ts +++ b/src/harness/unittests/extractRanges.ts @@ -365,6 +365,58 @@ 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("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/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/harness/unittests/reuseProgramStructure.ts b/src/harness/unittests/reuseProgramStructure.ts index 4a76a479b18..454e97b3133 100644 --- a/src/harness/unittests/reuseProgramStructure.ts +++ b/src/harness/unittests/reuseProgramStructure.ts @@ -884,7 +884,6 @@ namespace ts { }); }); - import TestSystem = ts.TestFSWithWatch.TestServerHost; type FileOrFolder = ts.TestFSWithWatch.FileOrFolder; import createTestSystem = ts.TestFSWithWatch.createWatchedSystem; import libFile = ts.TestFSWithWatch.libFile; @@ -910,30 +909,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 = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, system)).getCurrentProgram().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 = createWatchProgram(createWatchCompilerHostOfConfigFile(configFileName, {}, system)).getCurrentProgram().getProgram(); + const { fileNames, options } = parseConfigFileWithSystem(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", () => { @@ -1044,11 +1034,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/session.ts b/src/harness/unittests/session.ts index f46e173716c..7d1e5b0816c 100644 --- a/src/harness/unittests/session.ts +++ b/src/harness/unittests/session.ts @@ -4,6 +4,7 @@ const expect: typeof _chai.expect = _chai.expect; namespace ts.server { let lastWrittenToHost: string; + const noopFileWatcher: FileWatcher = { close: noop }; const mockHost: ServerHost = { args: [], newLine: "\n", @@ -26,6 +27,8 @@ namespace ts.server { setImmediate: () => 0, clearImmediate: noop, createHash: Harness.mockHash, + watchFile: () => noopFileWatcher, + watchDirectory: () => noopFileWatcher }; class TestSession extends Session { diff --git a/src/harness/unittests/tscWatchMode.ts b/src/harness/unittests/tscWatchMode.ts index 9e01021155c..a2b79b00dc7 100644 --- a/src/harness/unittests/tscWatchMode.ts +++ b/src/harness/unittests/tscWatchMode.ts @@ -22,24 +22,16 @@ 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, maxNumberOfFilesToIterateForInvalidation?: number) { + const compilerHost = ts.createWatchCompilerHostOfConfigFile(configFileName, {}, host); + compilerHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; + const watch = createWatchProgram(compilerHost); + return () => watch.getCurrentProgram().getProgram(); } - function parseConfigFile(configFileName: string, watchingSystemHost: WatchingSystemHost) { - return ts.parseConfigFile(configFileName, {}, watchingSystemHost.system, watchingSystemHost.reportDiagnostic, watchingSystemHost.reportWatchDiagnostic); - } - - function createWatchModeWithConfigFile(configFilePath: string, host: WatchedSystem, maxNumberOfFilesToIterateForInvalidation?: number) { - const watchingSystemHost = createWatchingSystemHost(host); - watchingSystemHost.maxNumberOfFilesToIterateForInvalidation = maxNumberOfFilesToIterateForInvalidation; - 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 = createWatchProgram(createWatchCompilerHostOfFilesAndCompilerOptions(rootFiles, options, host)); + return () => watch.getCurrentProgram().getProgram(); } function getEmittedLineForMultiFileOutput(file: FileOrFolder, host: WatchedSystem) { @@ -218,7 +210,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]); @@ -243,7 +235,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))]); }); @@ -272,14 +264,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 = createWatchProgram(createWatchCompilerHostOfConfigFile(configFile.path, {}, host, /*createProgram*/ undefined, notImplemented)); - const watch = ts.createWatchModeWithConfigFile(configFileResult, {}, watchingSystemHost); - - checkProgramActualFiles(watch(), [file1.path, libFile.path, file2.path]); - checkProgramRootFiles(watch(), [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); @@ -295,7 +283,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); @@ -319,7 +307,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]); @@ -332,7 +320,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 @@ -354,7 +342,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]); @@ -380,7 +368,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 = `{ @@ -407,7 +395,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]); }); @@ -435,7 +423,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]); @@ -463,7 +451,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]); }); @@ -481,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); checkProgramRootFiles(watch(), [file1.path]); checkProgramActualFiles(watch(), [file1.path, file2.path]); @@ -510,7 +498,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]); @@ -533,7 +521,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]); @@ -561,7 +549,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]); @@ -583,10 +571,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 @@ -609,7 +597,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]); @@ -634,7 +622,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]); @@ -664,7 +652,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 = { @@ -692,7 +680,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); @@ -716,7 +704,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]); }); @@ -766,7 +754,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]); @@ -791,7 +779,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]); }); @@ -805,7 +793,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const moduleFileOldPath = moduleFile.path; @@ -837,7 +825,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const moduleFileOldPath = moduleFile.path; @@ -872,7 +860,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]); }); @@ -887,7 +875,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") @@ -914,7 +902,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") @@ -934,7 +922,7 @@ namespace ts.tscWatch { }; const host = createWatchedSystem([file, configFile, libFile]); - createWatchModeWithConfigFile(configFile.path, host); + createWatchOfConfigFile(configFile.path, host); checkOutputErrors(host, emptyArray, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); }); @@ -951,7 +939,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); configFile.content = `{ @@ -987,7 +975,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]); }); @@ -1013,7 +1001,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]); }); @@ -1024,7 +1012,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]); }); @@ -1052,7 +1040,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") @@ -1089,7 +1077,7 @@ namespace ts.tscWatch { }) }; const host = createWatchedSystem([file1, file2, libFile, tsconfig], { currentDirectory: proj }); - const watch = createWatchModeWithConfigFile(tsconfig.path, host, /*maxNumberOfFilesToIterateForInvalidation*/1); + const watch = createWatchOfConfigFile(tsconfig.path, host, /*maxNumberOfFilesToIterateForInvalidation*/1); checkProgramActualFiles(watch(), [file1.path, file2.path, libFile.path]); assert.isTrue(host.fileExists("build/file1.js")); @@ -1138,7 +1126,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); @@ -1200,7 +1188,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); @@ -1284,7 +1272,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); } @@ -1595,11 +1583,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); @@ -1719,7 +1707,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"); @@ -1820,7 +1808,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, [ @@ -1862,7 +1850,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); @@ -1911,7 +1899,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") @@ -1953,7 +1941,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") @@ -1995,7 +1983,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, /*errorsPosition*/ ExpectedOutputErrorsPosition.AfterCompilationStarting); const expectedFiles: ExpectedFile[] = [ @@ -2072,7 +2060,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;"; @@ -2094,7 +2082,7 @@ declare module "fs" { }; const host = createWatchedSystem([file]); - createWatchModeWithoutConfigFile([file.path], host); + createWatchOfFilesAndCompilerOptions([file.path], host); host.runQueuedTimeoutCallbacks(); host.checkScreenClears(1); @@ -2106,7 +2094,7 @@ declare module "fs" { content: "" }; const host = createWatchedSystem([file]); - createWatchModeWithoutConfigFile([file.path], host); + createWatchOfFilesAndCompilerOptions([file.path], host); const modifiedFile = { ...file, 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/harness/virtualFileSystemWithWatch.ts b/src/harness/virtualFileSystemWithWatch.ts index 90129e8e0f5..381917d71c7 100644 --- a/src/harness/virtualFileSystemWithWatch.ts +++ b/src/harness/virtualFileSystemWithWatch.ts @@ -479,7 +479,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/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/chs/diagnosticMessages/diagnosticMessages.generated.json.lcl index a678f69e80e..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 @@ - + @@ -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 f6a140a47f7..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 @@ - + @@ -573,6 +573,15 @@ + + + + + + + + + @@ -888,6 +897,9 @@ + + + @@ -3366,15 +3378,6 @@ - - - - - - - - - @@ -3561,6 +3564,15 @@ + + + + + + + + + @@ -4620,6 +4632,9 @@ + + + @@ -6588,11 +6603,11 @@ - + - + - + diff --git a/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/csy/diagnosticMessages/diagnosticMessages.generated.json.lcl index 66702cf999d..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 @@ - + @@ -582,6 +582,15 @@ + + + + + + + + + @@ -897,6 +906,9 @@ + + + @@ -3375,15 +3387,6 @@ - - - - - - - - - @@ -3570,6 +3573,15 @@ + + + + + + + + + @@ -4629,6 +4641,9 @@ + + + @@ -6597,11 +6612,11 @@ - + - + - + diff --git a/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/deu/diagnosticMessages/diagnosticMessages.generated.json.lcl index 00fd63f90f9..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 @@ - + @@ -573,6 +573,15 @@ + + + + + + + + + @@ -885,6 +894,9 @@ + + + @@ -3363,15 +3375,6 @@ - - - - - - - - - @@ -3558,6 +3561,15 @@ + + + + + + + + + @@ -4617,6 +4629,9 @@ + + + @@ -6579,11 +6594,11 @@ - + - + - + diff --git a/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/esn/diagnosticMessages/diagnosticMessages.generated.json.lcl index 93c54090734..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 @@ - + @@ -582,6 +582,15 @@ + + + + + + + + + @@ -897,6 +906,9 @@ + + + @@ -3375,15 +3387,6 @@ - - - - - - - - - @@ -3570,6 +3573,15 @@ + + + + + + + + + @@ -4629,6 +4641,9 @@ + + + @@ -6597,11 +6612,11 @@ - + - + - + diff --git a/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/fra/diagnosticMessages/diagnosticMessages.generated.json.lcl index d6048a235f5..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 @@ - + @@ -582,6 +582,15 @@ + + + + + + + + + @@ -897,6 +906,9 @@ + + + @@ -3375,15 +3387,6 @@ - - - - - - - - - @@ -3570,6 +3573,15 @@ + + + + + + + + + @@ -4629,6 +4641,9 @@ + + + @@ -6597,11 +6612,11 @@ - + - + - + diff --git a/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl b/src/loc/lcl/ita/diagnosticMessages/diagnosticMessages.generated.json.lcl index 63f0270005a..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 @@ - + @@ -573,6 +573,15 @@ + + + + + + + + + @@ -3369,15 +3378,6 @@ - - - - - - - - - @@ -3564,6 +3564,15 @@ + + + + + + + + + @@ -6594,11 +6603,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..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 @@ - + @@ -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 dcd9d3695fc..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 @@ - + @@ -573,6 +573,15 @@ + + + + + + + + + @@ -888,6 +897,9 @@ + + + @@ -3366,15 +3378,6 @@ - - - - - - - - - @@ -3561,6 +3564,15 @@ + + + + + + + + + @@ -4620,6 +4632,9 @@ + + + @@ -6588,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 a36010b60e3..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 @@ - + @@ -566,6 +566,15 @@ + + + + + + + + + @@ -878,6 +887,9 @@ + + + @@ -3356,15 +3368,6 @@ - - - - - - - - - @@ -3551,6 +3554,15 @@ + + + + + + + + + @@ -4610,6 +4622,9 @@ + + + @@ -6572,11 +6587,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..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 @@ - + @@ -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 a8529c74fd0..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 @@ - + @@ -572,6 +572,15 @@ + + + + + + + + + @@ -887,6 +896,9 @@ + + + @@ -3365,15 +3377,6 @@ - - - - - - - - - @@ -3560,6 +3563,15 @@ + + + + + + + + + @@ -4619,6 +4631,9 @@ + + + @@ -6587,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 c8c49d28117..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 @@ - + @@ -566,6 +566,15 @@ + + + + + + + + + @@ -881,6 +890,9 @@ + + + @@ -3359,15 +3371,6 @@ - - - - - - - - - @@ -3554,6 +3557,15 @@ + + + + + + + + + @@ -4613,6 +4625,9 @@ + + + @@ -6581,11 +6596,11 @@ - + - + - + diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 4b6a95e3a82..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(); @@ -1495,7 +1495,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); @@ -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; } @@ -1800,19 +1800,19 @@ 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); 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; diff --git a/src/server/project.ts b/src/server/project.ts index a25e6870bda..140bab4fc06 100644 --- a/src/server/project.ts +++ b/src/server/project.ts @@ -3,7 +3,7 @@ /// /// /// -/// +/// namespace ts.server { @@ -140,7 +140,7 @@ namespace ts.server { /*@internal*/ resolutionCache: ResolutionCache; - private builder: Builder; + private builderState: BuilderState | undefined; /** * Set of files names that were updated since the last call to getChangesSinceVersion. */ @@ -202,6 +202,9 @@ namespace ts.server { /*@internal*/ readonly currentDirectory: string; + /*@internal*/ + public directoryStructureHost: DirectoryStructureHost; + /*@internal*/ constructor( /*@internal*/readonly projectName: string, @@ -212,8 +215,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); @@ -238,7 +242,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(); @@ -267,7 +271,7 @@ namespace ts.server { } getNewLine() { - return this.directoryStructureHost.newLine; + return this.projectService.host.newLine; } getProjectVersion() { @@ -335,7 +339,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[] { @@ -343,7 +347,7 @@ namespace ts.server { } readFile(fileName: string): string | undefined { - return this.directoryStructureHost.readFile(fileName); + return this.projectService.host.readFile(fileName); } fileExists(file: string): boolean { @@ -354,7 +358,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[] { @@ -369,6 +373,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); @@ -443,15 +452,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(); } @@ -461,8 +461,8 @@ namespace ts.server { return []; } this.updateGraph(); - this.ensureBuilder(); - return mapDefined(this.builder.getFilesAffectedBy(this.program, scriptInfo.path), + 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); } @@ -498,6 +498,7 @@ namespace ts.server { } this.languageService.cleanupSemanticCache(); this.languageServiceEnabled = false; + this.builderState = undefined; this.resolutionCache.closeTypeRootsWatch(); this.projectService.onUpdateLanguageServiceStateForProject(this, /*languageServiceEnabled*/ false); } @@ -557,7 +558,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; @@ -813,15 +814,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) { @@ -921,7 +916,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 051279ff3cb..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 { @@ -345,7 +345,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/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/src/services/codeFixProvider.ts b/src/services/codeFixProvider.ts index aa1f2fb6885..502f8457d8c 100644 --- a/src/services/codeFixProvider.ts +++ b/src/services/codeFixProvider.ts @@ -10,7 +10,6 @@ namespace ts { export interface CodeFixContextBase extends textChanges.TextChangesContext { sourceFile: SourceFile; program: Program; - host: LanguageServiceHost; cancellationToken: CancellationToken; } diff --git a/src/services/codefixes/disableJsDiagnostics.ts b/src/services/codefixes/disableJsDiagnostics.ts index 6a59e61d52d..3bcbf886df8 100644 --- a/src/services/codefixes/disableJsDiagnostics.ts +++ b/src/services/codefixes/disableJsDiagnostics.ts @@ -1,23 +1,25 @@ /* @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; }); 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 = getNewLineOrDefaultFromHost(context.host, context.formatContext.options); + 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, }, { @@ -33,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, context.newLineCharacter)); - } - }), + 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. @@ -52,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/src/services/codefixes/fixAddMissingMember.ts b/src/services/codefixes/fixAddMissingMember.ts index 0fc6a430e19..f7f5aa0a22f 100644 --- a/src/services/codefixes/fixAddMissingMember.ts +++ b/src/services/codefixes/fixAddMissingMember.ts @@ -142,7 +142,7 @@ 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)); 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( @@ -181,7 +181,7 @@ namespace ts.codefix { 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)); return { description, changes, fixId }; diff --git a/src/services/codefixes/importFixes.ts b/src/services/codefixes/importFixes.ts index 8f69cd95c11..ac88d98eba5 100644 --- a/src/services/codefixes/importFixes.ts +++ b/src/services/codefixes/importFixes.ts @@ -29,12 +29,8 @@ namespace ts.codefix { symbolName: string; } - interface SymbolAndTokenContext extends SymbolContext { + interface ImportCodeFixContext extends SymbolContext { symbolToken: Identifier | undefined; - } - - interface ImportCodeFixContext extends SymbolAndTokenContext { - host: LanguageServiceHost; program: Program; checker: TypeChecker; compilerOptions: CompilerOptions; @@ -173,7 +169,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, @@ -395,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.) @@ -472,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); diff --git a/src/services/completions.ts b/src/services/completions.ts index 36a10a1b578..bce1eadaf26 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -20,6 +20,8 @@ namespace ts.Completions { None, ClassElementKeywords, // Keywords at class keyword ConstructorParameterKeywords, // Keywords at constructor parameter + FunctionLikeBodyKeywords, // Keywords at function like body + TypeKeywords, } export function getCompletionsAtPosition( @@ -76,7 +78,7 @@ namespace ts.Completions { } function completionInfoFromData(sourceFile: SourceFile, typeChecker: TypeChecker, compilerOptions: CompilerOptions, log: Log, completionData: CompletionData, includeInsertTextCompletions: boolean): CompletionInfo { - const { symbols, completionKind, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion } = completionData; + const { symbols, completionKind, isNewIdentifierLocation, location, propertyAccessToConvert, keywordFilters, symbolToOriginInfoMap, recommendedCompletion, isJsxInitializer } = completionData; if (sourceFile.languageVariant === LanguageVariant.JSX && location && location.parent && isJsxClosingElement(location.parent)) { // In the TypeScript JSX element, if such element is not defined. When users query for completion at closing tag, @@ -97,7 +99,7 @@ namespace ts.Completions { const entries: CompletionEntry[] = []; if (isSourceFileJavaScript(sourceFile)) { - const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, recommendedCompletion, symbolToOriginInfoMap); + const uniqueNames = getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); getJavaScriptCompletionEntries(sourceFile, location.pos, uniqueNames, compilerOptions.target, entries); } else { @@ -105,7 +107,7 @@ namespace ts.Completions { return undefined; } - getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, recommendedCompletion, symbolToOriginInfoMap); + getCompletionEntriesFromSymbols(symbols, entries, location, sourceFile, typeChecker, compilerOptions.target, log, completionKind, includeInsertTextCompletions, propertyAccessToConvert, isJsxInitializer, recommendedCompletion, symbolToOriginInfoMap); } // TODO add filter for keyword based on type/value/namespace and also location @@ -165,6 +167,7 @@ namespace ts.Completions { origin: SymbolOriginInfo | undefined, recommendedCompletion: Symbol | undefined, propertyAccessToConvert: PropertyAccessExpression | undefined, + isJsxInitializer: boolean, includeInsertTextCompletions: boolean, ): CompletionEntry | undefined { const info = getCompletionEntryDisplayNameForSymbol(symbol, target, origin, kind); @@ -172,19 +175,27 @@ namespace ts.Completions { return undefined; } const { name, needsConvertPropertyAccess } = info; - if (needsConvertPropertyAccess && !includeInsertTextCompletions) { - return undefined; - } let insertText: string | undefined; let replacementSpan: TextSpan | undefined; - if (kind === CompletionKind.Global && origin && origin.type === "this-type") { - insertText = needsConvertPropertyAccess ? `this["${name}"]` : `this.${name}`; + if (includeInsertTextCompletions) { + if (origin && origin.type === "this-type") { + insertText = needsConvertPropertyAccess ? `this["${name}"]` : `this.${name}`; + } + else if (needsConvertPropertyAccess) { + // TODO: GH#20619 Use configured quote style + insertText = `["${name}"]`; + replacementSpan = createTextSpanFromBounds(findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!.getStart(sourceFile), propertyAccessToConvert!.name.end); + } + + if (isJsxInitializer) { + if (insertText === undefined) insertText = name; + insertText = `{${insertText}}`; + } } - else if (needsConvertPropertyAccess) { - // TODO: GH#20619 Use configured quote style - insertText = `["${name}"]`; - replacementSpan = createTextSpanFromBounds(findChildOfKind(propertyAccessToConvert!, SyntaxKind.DotToken, sourceFile)!.getStart(sourceFile), propertyAccessToConvert!.name.end); + + if (insertText !== undefined && !includeInsertTextCompletions) { + return undefined; } // TODO(drosen): Right now we just permit *all* semantic meanings when calling @@ -233,6 +244,7 @@ namespace ts.Completions { kind: CompletionKind, includeInsertTextCompletions?: boolean, propertyAccessToConvert?: PropertyAccessExpression | undefined, + isJsxInitializer?: boolean, recommendedCompletion?: Symbol, symbolToOriginInfoMap?: SymbolOriginInfoMap, ): Map { @@ -244,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; } @@ -481,6 +493,7 @@ namespace ts.Completions { location: Node; symbolToOriginInfoMap: SymbolOriginInfoMap; previousToken: Node; + readonly isJsxInitializer: boolean; } function getSymbolCompletionFromEntryId( typeChecker: TypeChecker, @@ -499,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 @@ -508,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" }; } @@ -564,7 +577,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, @@ -627,7 +640,6 @@ namespace ts.Completions { host, program, checker, - newLineCharacter: host.getNewLine(), compilerOptions, sourceFile, formatContext, @@ -677,11 +689,13 @@ 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 }; const enum CompletionKind { ObjectPropertyDeclaration, + /** Note that sometimes we access completions from global scope, but use "None" instead of this. See isGlobalCompletionScope. */ Global, PropertyAccess, MemberLike, @@ -857,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) { @@ -915,6 +930,10 @@ namespace ts.Completions { location = contextToken; } break; + + case SyntaxKind.JsxAttribute: + isJsxInitializer = previousToken.kind === SyntaxKind.EqualsToken; + break; } } } @@ -960,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; @@ -1061,6 +1080,10 @@ namespace ts.Completions { return true; } + if (tryGetFunctionLikeBodyCompletionContainer(contextToken)) { + keywordFilters = KeywordCompletionFilters.FunctionLikeBodyKeywords; + } + if (classLikeContainer = tryGetClassLikeCompletionContainer(contextToken)) { // cursor inside class declaration getGetClassLikeCompletionSymbols(classLikeContainer); @@ -1159,6 +1182,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 @@ -1166,19 +1192,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); } @@ -1195,7 +1216,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) { @@ -1213,6 +1234,7 @@ namespace ts.Completions { return parentKind === SyntaxKind.AsExpression; } } + return false; } function symbolCanBeReferencedAtTypeLocation(symbol: Symbol): boolean { @@ -1689,6 +1711,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; @@ -2092,13 +2130,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 }; @@ -2110,49 +2148,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 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) { @@ -2189,6 +2216,23 @@ 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: + case SyntaxKind.UndefinedKeyword: + return false; + } + return true; + } + function isEqualityOperatorKind(kind: ts.SyntaxKind): kind is EqualityOperator { switch (kind) { case ts.SyntaxKind.EqualsEqualsEqualsToken: diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index 8b7473357fe..31a0702bb82 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) }); } @@ -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/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/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; } } 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; diff --git a/src/services/refactorProvider.ts b/src/services/refactorProvider.ts index 85ef9113bda..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; @@ -19,7 +13,6 @@ namespace ts { startPosition: number; endPosition?: number; program: Program; - host: LanguageServiceHost; cancellationToken?: CancellationToken; } @@ -28,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..e3a2763b783 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 }); @@ -241,6 +235,16 @@ 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. + return { errors: [createFileDiagnostic(sourceFile, span.start, length, Messages.cannotExtractRange)] }; + } + return { targetRange: { range: statements, facts: rangeFacts, declarations } }; } @@ -1327,6 +1331,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/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, }, ], diff --git a/src/services/services.ts b/src/services/services.ts index 4236416fbb3..00442f73b04 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, @@ -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, diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 467ba6735ca..3674cd31b97 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -187,7 +187,7 @@ namespace ts.textChanges { } export interface TextChangesContext { - newLineCharacter: string; + host: LanguageServiceHost; formatContext: ts.formatting.FormatContext; } @@ -199,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(context.newLineCharacter === "\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/tsconfig.json b/src/services/tsconfig.json index d73014a93a2..13a7a30d845 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -1,4 +1,4 @@ -{ +{ "extends": "../tsconfig-base", "compilerOptions": { "removeComments": false, @@ -37,6 +37,11 @@ "../compiler/declarationEmitter.ts", "../compiler/emitter.ts", "../compiler/program.ts", + "../compiler/builderState.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/src/services/utilities.ts b/src/services/utilities.ts index 344a44a9f18..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 { @@ -1259,8 +1263,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() { diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index 887fb50f76a..c1af8fc036f 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; @@ -2807,12 +2808,13 @@ declare namespace ts { } } declare namespace ts { - const versionMajorMinor = "2.7"; + const versionMajorMinor = "2.8"; /** The version of the TypeScript compiler release */ const version: string; } 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; @@ -2829,26 +2831,14 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - /** - * Partial interface of the System thats needed to support the caching of directory structure - */ - interface DirectoryStructureHost { + interface System { + args: string[]; 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; - 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 { - args: string[]; 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 @@ -2856,7 +2846,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. @@ -2864,6 +2860,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; @@ -3870,17 +3867,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; - } -} declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string | undefined; function resolveTripleslashReference(moduleName: string, containingFile: string): string; @@ -4815,6 +4801,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; @@ -7351,6 +7339,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, @@ -7394,7 +7393,6 @@ declare namespace ts.server { private documentRegistry; private compilerOptions; compileOnSaveEnabled: boolean; - directoryStructureHost: DirectoryStructureHost; private rootFiles; private rootFilesMap; private program; @@ -7407,7 +7405,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. */ @@ -7468,7 +7466,6 @@ declare namespace ts.server { getGlobalProjectErrors(): ReadonlyArray; getAllProjectErrors(): ReadonlyArray; getLanguageService(ensureSynchronized?: boolean): LanguageService; - private ensureBuilder(); private shouldEmitFile(scriptInfo); getCompileOnSaveAffectedFileList(scriptInfo: ScriptInfo): string[]; /** @@ -7863,7 +7860,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 d5259f39f7f..4f84f50e3bb 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; @@ -2807,12 +2808,13 @@ declare namespace ts { } } declare namespace ts { - const versionMajorMinor = "2.7"; + const versionMajorMinor = "2.8"; /** The version of the TypeScript compiler release */ const version: string; } 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; @@ -2829,26 +2831,14 @@ declare namespace ts { callback: FileWatcherCallback; mtime?: Date; } - /** - * Partial interface of the System thats needed to support the caching of directory structure - */ - interface DirectoryStructureHost { + interface System { + args: string[]; 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; - 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 { - args: string[]; 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 @@ -2856,7 +2846,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. @@ -2864,6 +2860,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; @@ -3817,17 +3814,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; - } -} declare namespace ts { function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean, configName?: string): string | undefined; function resolveTripleslashReference(moduleName: string, containingFile: string): string; @@ -3857,6 +3843,258 @@ declare namespace ts { */ function createProgram(rootNames: ReadonlyArray, options: CompilerOptions, host?: CompilerHost, oldProgram?: Program): Program; } +declare namespace ts { + interface EmitOutput { + outputFiles: OutputFile[]; + emitSkipped: boolean; + } + interface OutputFile { + name: string; + writeByteOrderMark: boolean; + text: string; + } +} +declare namespace ts { + type AffectedFileResult = { + result: T; + affected: SourceFile | Program; + } | undefined; + interface BuilderProgramHost { + /** + * 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; + /** + * 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 BuilderProgram { + /** + * Returns current program + */ + getProgram(): Program; + /** + * Get compiler options of the program + */ + 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(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; + /** + * 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 + */ + 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 + */ + 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 EmitAndSemanticDiagnosticsBuilderProgram extends BuilderProgram { + /** + * 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(writeFile?: WriteFileCallback, cancellationToken?: CancellationToken, emitOnlyDtsFiles?: boolean, customTransformers?: CustomTransformers): AffectedFileResult; + } + /** + * 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; + 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: T): 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; + 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 + */ + 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 would be used to write log about compilation */ + trace?(s: string): void; + /** If provided is used to get the environment variable */ + getEnvironmentVariable?(name: string): string; + /** 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 | undefined)[]; + /** 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; + /** 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 + */ + interface WatchCompilerHostOfFilesAndCompilerOptions extends WatchCompilerHost { + /** root files to use to generate program */ + rootFiles: string[]; + /** Compiler options */ + options: CompilerOptions; + } + /** + * Reports config file diagnostics + */ + interface ConfigFileDiagnosticsReporter { + /** + * Reports the diagnostics in reading/writing or parsing of the config file + */ + onConfigFileDiagnostic: DiagnosticReporter; + /** + * Reports unrecoverable error when parsing config file + */ + onUnRecoverableConfigFileDiagnostic: DiagnosticReporter; + } + /** + * Host to create watch with config file + */ + interface WatchCompilerHostOfConfigFile extends WatchCompilerHost, ConfigFileDiagnosticsReporter { + /** Name of the config file to compile */ + configFileName: string; + /** Options to extend */ + optionsToExtend?: CompilerOptions; + /** + * 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 */ + getProgram(): T; + } + /** + * 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 watch compiler host for either configFile or fileNames and its options + */ + 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; + /** + * Creates the watch from the host for config file + */ + function createWatchProgram(host: WatchCompilerHostOfConfigFile): WatchOfConfigFile; +} declare namespace ts { function parseCommandLine(commandLine: ReadonlyArray, readFile?: (path: string) => string | undefined): ParsedCommandLine; /** 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/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/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/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/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; +} 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/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/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/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/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/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/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt new file mode 100644 index 00000000000..b2fea589cb1 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.errors.txt @@ -0,0 +1,92 @@ +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(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(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 + + declare function f(a:number,b:number): void; + + 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 + } + + 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'. + } + + declare function useBar(bar: number): void; + + 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'. + } + + 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 new file mode 100644 index 00000000000..642f00a4f50 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.js @@ -0,0 +1,108 @@ +//// [optionalParameterInDestructuringWithInitializer.ts] +// https://github.com/Microsoft/TypeScript/issues/17080 + +declare function f(a:number,b:number): void; + +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 +} + +interface Foo { + readonly bar?: number; +} + +function performFoo({ bar }: Foo = {}) { + useBar(bar); +} + +declare function useBar(bar: number): void; + +performFoo(); + +function performFoo2({ bar = null }: Foo = {}) { + useBar2(bar); +} + +declare function useBar2(bar: number | undefined): void; + +performFoo2(); + + +//// [optionalParameterInDestructuringWithInitializer.js] +// https://github.com/Microsoft/TypeScript/issues/17080 +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 +} +function performFoo(_a) { + var bar = (_a === void 0 ? {} : _a).bar; + useBar(bar); +} +performFoo(); +function performFoo2(_a) { + var _b = (_a === void 0 ? {} : _a).bar, bar = _b === void 0 ? null : _b; + useBar2(bar); +} +performFoo2(); diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols new file mode 100644 index 00000000000..6d4cf484d92 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.symbols @@ -0,0 +1,195 @@ +=== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === +// https://github.com/Microsoft/TypeScript/issues/17080 + +declare function f(a:number,b:number): void; +>f : Symbol(f, Decl(optionalParameterInDestructuringWithInitializer.ts, 0, 0)) +>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, 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)) +>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 +} + +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)) +} + +declare function useBar(bar: number): void; +>useBar : Symbol(useBar, Decl(optionalParameterInDestructuringWithInitializer.ts, 45, 1)) +>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, 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, 53, 1)) +>bar : Symbol(bar, Decl(optionalParameterInDestructuringWithInitializer.ts, 51, 22)) +} + +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, 49, 13)) + diff --git a/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types new file mode 100644 index 00000000000..26110e71051 --- /dev/null +++ b/tests/baselines/reference/optionalParameterInDestructuringWithInitializer.types @@ -0,0 +1,246 @@ +=== tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts === +// https://github.com/Microsoft/TypeScript/issues/17080 + +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 +>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 +} + +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 +} + +declare function useBar(bar: number): void; +>useBar : (bar: number) => void +>bar : number + +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 +} + +declare function useBar2(bar: number | undefined): void; +>useBar2 : (bar: number | undefined) => void +>bar : number | undefined + +performFoo2(); +>performFoo2() : void +>performFoo2 : ({ bar }?: Foo) => void + 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/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" + 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/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/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/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 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/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 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); 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) => {}; diff --git a/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts new file mode 100644 index 00000000000..d3ebd99efa1 --- /dev/null +++ b/tests/cases/compiler/optionalParameterInDestructuringWithInitializer.ts @@ -0,0 +1,59 @@ +// @strictNullChecks: true +// https://github.com/Microsoft/TypeScript/issues/17080 + +declare function f(a:number,b:number): void; + +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 +} + +interface Foo { + readonly bar?: number; +} + +function performFoo({ bar }: Foo = {}) { + useBar(bar); +} + +declare function useBar(bar: number): void; + +performFoo(); + +function performFoo2({ bar = null }: Foo = {}) { + useBar2(bar); +} + +declare function useBar2(bar: number | undefined): void; + +performFoo2(); 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" }); 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 { + ; +} 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 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 = ; 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 +} 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..f5bdf567b20 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember.ts @@ -9,9 +9,8 @@ verify.codeFix({ description: "Declare property 'foo'", 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..a4b7ffb3bfe 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember2.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember2.ts @@ -9,9 +9,8 @@ verify.codeFix({ description: "Add index signature for property 'foo'", 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..82512406985 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember3.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember3.ts @@ -9,9 +9,8 @@ verify.codeFix({ description: "Declare static property 'foo'", 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..58348bca053 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember4.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember4.ts @@ -15,10 +15,9 @@ verify.codeFix({ description: "Initialize property 'foo' in the constructor", 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..64a62b8268a 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember5.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember5.ts @@ -13,12 +13,11 @@ verify.codeFix({ description: "Initialize static property 'foo'", index: 0, - // TODO: GH#18445 newFileContent: `class C { 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..902cafdfb24 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember6.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember6.ts @@ -13,10 +13,9 @@ verify.codeFix({ description: "Initialize property 'foo' in the constructor", 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..6b80901a7bb 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember7.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember7.ts @@ -11,10 +11,9 @@ verify.codeFix({ description: "Initialize static property 'foo'", index: 2, - // 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..ae5e957732b 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all.ts @@ -11,12 +11,11 @@ verify.codeFixAll({ fixId: "addMissingMember", 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..305b6d87c30 100644 --- a/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts +++ b/tests/cases/fourslash/codeFixAddMissingMember_all_js.ts @@ -16,13 +16,12 @@ verify.codeFixAll({ fixId: "addMissingMember", 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..9521173ad89 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractMethod_all.ts @@ -8,19 +8,18 @@ verify.codeFixAll({ fixId: "fixClassDoesntImplementInheritedAbstractMember", - // TODO: GH#18445 newFileContent: `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..06b9dddcd8a 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPropertyThis.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { 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..a150dd07f8d 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractProtectedProperty.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { 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..48a023e96fa 100644 --- a/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts +++ b/tests/cases/fourslash/codeFixClassExtendAbstractPublicProperty.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Implement inherited abstract class", - // TODO: GH#18445 newFileContent: `abstract class A { 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..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; @@ -28,9 +27,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..115517f4e6d 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassFunctionVoidInferred.ts @@ -8,15 +8,14 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { 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..a0b59c6372c 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassMultipleSignatures1.ts @@ -8,16 +8,15 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `class A { 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..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; @@ -18,12 +17,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..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; @@ -20,9 +19,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..3c34de1e1bf 100644 --- a/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts +++ b/tests/cases/fourslash/codeFixClassImplementClassPropertyTypeQuery.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'A'", - // TODO: GH#18445 newFileContent: `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..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 } @@ -75,12 +74,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..8793872ce95 100644 --- a/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts +++ b/tests/cases/fourslash/codeFixClassImplementDefaultClass.ts @@ -5,10 +5,9 @@ verify.codeFix({ description: "Implement interface 'I'", - // 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..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[]; @@ -18,9 +17,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..cd9b689736d 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceClassExpression.ts @@ -6,10 +6,9 @@ verify.codeFix({ description: "Implement interface 'I'", - // 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..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 */ @@ -36,11 +35,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..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; @@ -20,14 +19,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..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; @@ -38,34 +37,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..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 { @@ -24,9 +23,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..15fdcdab913 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesBoth.ts @@ -10,15 +10,14 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [x: number]: I; [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..ede4ed4f568 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesNumber.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `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..2e2c49d2700 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexSignaturesString.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { [Ƚ: 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..2a2b4c32c1b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceIndexType.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `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..516919b13df 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceInheritsAbstractMethod.ts @@ -9,16 +9,15 @@ verify.codeFix({ description: "Implement interface 'I1'", - // TODO: GH#18445 newFileContent: `abstract class C1 { } 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..17f8ccb0bea 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMappedType.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `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..722fe51d0a5 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberNestedTypeAlias.ts @@ -9,17 +9,16 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `type Either = { val: T } | Error; 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..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 { @@ -62,30 +61,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..6fd8900d51b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMemberTypeAlias.ts @@ -6,14 +6,13 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 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..4dfeb7cd95b 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodThisAndSelfReference.ts @@ -8,15 +8,14 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { 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..45bd6096142 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceMethodTypePredicate.ts @@ -9,18 +9,17 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `interface I { f(i: any): i is I; 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..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, @@ -24,18 +23,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..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; @@ -18,12 +17,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..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; @@ -18,12 +17,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..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; @@ -18,12 +17,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..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; } @@ -18,7 +17,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..9cb6f477733 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceOptionalProperty.ts @@ -8,14 +8,13 @@ verify.codeFix({ description: "Implement interface 'IPerson'", - // TODO: GH#18445 newFileContent: `interface IPerson { 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..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 { @@ -22,10 +21,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..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: {}; @@ -40,17 +39,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..b587d937062 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceQualifiedName.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'N.I'", - // TODO: GH#18445 newFileContent: `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..cf8926ff33e 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateDeeply.ts @@ -7,12 +7,11 @@ verify.codeFix({ description: "Implement interface 'I'", - // TODO: GH#18445 newFileContent: `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..88f0d32fe18 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateNumber.ts @@ -5,10 +5,9 @@ verify.codeFix({ description: "Implement interface 'I'", - // 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..504b1684632 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateT.ts @@ -5,10 +5,9 @@ verify.codeFix({ description: "Implement interface 'I'", - // 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..c189fed4c8c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamInstantiateU.ts @@ -5,10 +5,9 @@ verify.codeFix({ description: "Implement interface 'I'", - // 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..70db2b5908c 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterfaceTypeParamMethod.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Implement interface 'I'", newFileContent: - // TODO: GH#18445 `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..cc27e395ed7 100644 --- a/tests/cases/fourslash/codeFixClassImplementInterface_all.ts +++ b/tests/cases/fourslash/codeFixClassImplementInterface_all.ts @@ -7,21 +7,21 @@ verify.codeFixAll({ fixId: "fixClassIncorrectlyImplementsInterface", - // TODO: GH#20073 GH#18445 + // TODO: GH#20073 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..5d1772ba8b8 100644 --- a/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts +++ b/tests/cases/fourslash/codeFixClassSuperMustPrecedeThisAccess.ts @@ -9,8 +9,7 @@ //// super(); //// |]} ////} -// 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..154eda0e084 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall.ts @@ -8,13 +8,12 @@ verify.codeFix({ description: "Add missing 'super()' call", - // TODO: GH#18445 newFileContent: `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..3833af62de0 100644 --- a/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts +++ b/tests/cases/fourslash/codeFixConstructorForDerivedNeedSuperCall_all.ts @@ -9,15 +9,14 @@ 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..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\r -x = 1; -// @ts-ignore\r -x = true;`, +// @ts-ignore +x = 1; x = true; +// @ts-ignore +x = [];`, }); diff --git a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts index b1fac4e0f12..0bd4ac3a879 100644 --- a/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredInStaticMethod.ts @@ -12,11 +12,10 @@ verify.codeFix({ description: "Declare static method 'm1'", 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 +23,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 +36,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 +50,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..70ed58b995b 100644 --- a/tests/cases/fourslash/codeFixUndeclaredMethod.ts +++ b/tests/cases/fourslash/codeFixUndeclaredMethod.ts @@ -13,11 +13,10 @@ verify.codeFix({ description: "Declare method 'foo1'", 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 +24,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 +37,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/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') 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/completionsImport_default_alreadyExistedWithRename.ts b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts index 09c0e74002b..75c45c2970b 100644 --- a/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts +++ b/tests/cases/fourslash/completionsImport_default_alreadyExistedWithRename.ts @@ -17,8 +17,7 @@ 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";\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..ca9d9579901 100644 --- a/tests/cases/fourslash/completionsImport_default_anonymous.ts +++ b/tests/cases/fourslash/completionsImport_default_anonymous.ts @@ -22,9 +22,8 @@ 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";\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..0e9edb4d1b2 100644 --- a/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts +++ b/tests/cases/fourslash/completionsImport_default_didNotExistBefore.ts @@ -18,8 +18,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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..45cdf71156e 100644 --- a/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts +++ b/tests/cases/fourslash/completionsImport_default_exportDefaultIdentifier.ts @@ -19,8 +19,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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..43789dea095 100644 --- a/tests/cases/fourslash/completionsImport_fromAmbientModule.ts +++ b/tests/cases/fourslash/completionsImport_fromAmbientModule.ts @@ -12,8 +12,7 @@ verify.applyCodeActionFromCompletion("", { name: "x", 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..da118826235 100644 --- a/tests/cases/fourslash/completionsImport_multipleWithSameName.ts +++ b/tests/cases/fourslash/completionsImport_multipleWithSameName.ts @@ -23,8 +23,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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..788c9695e45 100644 --- a/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts +++ b/tests/cases/fourslash/completionsImport_named_exportEqualsNamespace.ts @@ -19,8 +19,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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_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 5808e6c9f1a..3951a93a57f 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias.ts @@ -29,8 +29,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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..bc66d485877 100644 --- a/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts +++ b/tests/cases/fourslash/completionsImport_ofAlias_preferShortPath.ts @@ -24,8 +24,7 @@ verify.applyCodeActionFromCompletion("", { name: "foo", 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..c1830b726a1 100644 --- a/tests/cases/fourslash/completionsImport_require.ts +++ b/tests/cases/fourslash/completionsImport_require.ts @@ -23,9 +23,8 @@ verify.applyCodeActionFromCompletion("b", { name: "foo", 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`, }); @@ -40,9 +39,8 @@ verify.applyCodeActionFromCompletion("c", { name: "foo", 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/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"]}', +}); 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", }); 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"); 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"]); 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/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/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] }, +]); 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/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 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/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_; 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/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();`, ]); diff --git a/tests/cases/fourslash/importNameCodeFixReExport.ts b/tests/cases/fourslash/importNameCodeFixReExport.ts index eb1c1a91343..c77d32cc458 100644 --- a/tests/cases/fourslash/importNameCodeFixReExport.ts +++ b/tests/cases/fourslash/importNameCodeFixReExport.ts @@ -10,8 +10,7 @@ ////x;|] 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/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 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");