diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 766d0292a42..b1428a56bfa 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -961,14 +961,14 @@ export function createModuleResolutionLoader( ): ResolutionLoader { return { nameAndMode: moduleResolutionNameAndModeGetter, - resolve: (moduleName, resoluionMode) => resolveModuleName( + resolve: (moduleName, resolutionMode) => resolveModuleName( moduleName, containingFile, options, host, cache, redirectedReference, - resoluionMode, + resolutionMode, ), }; } diff --git a/src/server/session.ts b/src/server/session.ts index 820226c2f55..86b61f58e97 100644 --- a/src/server/session.ts +++ b/src/server/session.ts @@ -730,6 +730,12 @@ function getPerProjectReferences( if (resultsMap.has(project)) continue; if (isLocationProjectReferenceRedirect(project, location)) continue; + // The project could be dirty and could no longer contain the location's file after it's updated, + // so we need to update the project and check if it still contains the file. + updateProjectIfDirty(project); + if (!project.containsFile(toNormalizedPath(location.fileName))) { + continue; + } const projectResults = searchPosition(project, location); resultsMap.set(project, projectResults ?? emptyArray); searchedProjectKeys.add(getProjectKey(project)); diff --git a/src/testRunner/tests.ts b/src/testRunner/tests.ts index 01f8bd7465b..c95ca207db5 100644 --- a/src/testRunner/tests.ts +++ b/src/testRunner/tests.ts @@ -53,6 +53,7 @@ import "./unittests/services/extract/constants"; import "./unittests/services/extract/functions"; import "./unittests/services/extract/symbolWalker"; import "./unittests/services/extract/ranges"; +import "./unittests/services/findAllReferences"; import "./unittests/services/hostNewLineSupport"; import "./unittests/services/languageService"; import "./unittests/services/organizeImports"; diff --git a/src/testRunner/unittests/services/findAllReferences.ts b/src/testRunner/unittests/services/findAllReferences.ts new file mode 100644 index 00000000000..ec1ea57921d --- /dev/null +++ b/src/testRunner/unittests/services/findAllReferences.ts @@ -0,0 +1,141 @@ +import { protocol } from "../../_namespaces/ts.server"; +import { baselineTsserverLogs, createLoggerWithInMemoryLogs, createSession } from "../tsserver/helpers"; +import { createServerHost, File } from "../virtualFileSystemWithWatch"; + +describe("unittests:: services:: findAllReferences", () => { + it("does not try to open a file in a project that was updated and no longer has the file", () => { + const files: File[] = [ + { + path: "/packages/babel-loader/tsconfig.json", + content: +` +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"], + "references": [{"path": "../core"}] +} +` + }, + { + path: "/packages/babel-loader/src/index.ts", + content: +` +import type { Foo } from "../../core/src/index.js"; +` + }, + { + path: "/packages/core/tsconfig.json", + content: +` +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "./src", + "outDir": "./dist", + }, + "include": ["./src"] +} +` + }, + { + path: "/packages/core/src/index.ts", + content: +` +import { Bar } from "./loading-indicator.js"; +export type Foo = {}; +const bar: Bar = { + prop: 0 +} +` + }, + { + path: "/packages/core/src/loading-indicator.ts", + content: +` +export interface Bar { + prop: number; +} +const bar: Bar = { + prop: 1 +} +` + }, + ]; + const host = createServerHost(files); + const session = createSession(host, { logger: createLoggerWithInMemoryLogs(host) }); + // Open files in the two configured projects + session.executeCommandSeq({ + command: protocol.CommandTypes.UpdateOpen, + arguments: { + openFiles: [ + { + file: files[1].path, // babel-loader/src/index.ts + fileContent: files[1].content, + } + ] + } + }); + session.executeCommandSeq({ + command: protocol.CommandTypes.UpdateOpen, + arguments: { + openFiles: [ + { + file: files[3].path, // core/src/index.ts + fileContent: files[3].content, + } + ] + } + }); + // Now change `babel-loader` project to no longer import `core` project + session.executeCommandSeq({ + command: protocol.CommandTypes.UpdateOpen, + arguments: { + changedFiles: [ + { + fileName: files[1].path, + textChanges: [ + { + start: { + line: 1, + offset: 26 + }, + end: { + line: 1, + offset: 26 + }, + newText: "// comment", + } + ] + } + ] + } + }); + const loadingIndicatorScriptInfo = session.getProjectService().getScriptInfo(files[3].path)!; + // At this point, we haven't updated `babel-loader` project yet, + // so `babel-loader` is still a containing project of `loading-indicator` file. + assert(loadingIndicatorScriptInfo.containingProjects.find(p => p.projectName === "/packages/babel-loader/tsconfig.json")); + // When calling find all references, + // we shouldn't crash due to using outdated information on a file's containig projects. + session.executeCommandSeq({ + command: protocol.CommandTypes.References, + arguments: { + file: files[3].path, // core/src/index.ts + line: 5, // `prop` + offset: 5, + } + }); + baselineTsserverLogs("findAllReferences", "does not try to open a file in a project that was updated and no longer has the file", session); + }); +}); diff --git a/tests/baselines/reference/tsserver/findAllReferences/does-not-try-to-open-a-file-in-a-project-that-was-updated-and-no-longer-has-the-file.js b/tests/baselines/reference/tsserver/findAllReferences/does-not-try-to-open-a-file-in-a-project-that-was-updated-and-no-longer-has-the-file.js new file mode 100644 index 00000000000..bfb3f78c37f --- /dev/null +++ b/tests/baselines/reference/tsserver/findAllReferences/does-not-try-to-open-a-file-in-a-project-that-was-updated-and-no-longer-has-the-file.js @@ -0,0 +1,520 @@ +Info 0 [00:00:23.000] Provided types map file "/a/lib/typesMap.json" doesn't exist +Info 1 [00:00:24.000] request: + { + "command": "updateOpen", + "arguments": { + "openFiles": [ + { + "file": "/packages/babel-loader/src/index.ts", + "fileContent": "\nimport type { Foo } from \"../../core/src/index.js\";\n" + } + ] + }, + "seq": 1, + "type": "request" + } +Before request +//// [/packages/babel-loader/tsconfig.json] + +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "src", + "outDir": "dist" + }, + "include": ["src"], + "references": [{"path": "../core"}] +} + + +//// [/packages/babel-loader/src/index.ts] + +import type { Foo } from "../../core/src/index.js"; + + +//// [/packages/core/tsconfig.json] + +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "./src", + "outDir": "./dist", + }, + "include": ["./src"] +} + + +//// [/packages/core/src/index.ts] + +import { Bar } from "./loading-indicator.js"; +export type Foo = {}; +const bar: Bar = { + prop: 0 +} + + +//// [/packages/core/src/loading-indicator.ts] + +export interface Bar { + prop: number; +} +const bar: Bar = { + prop: 1 +} + + + +PolledWatches:: + +FsWatches:: + +FsWatchesRecursive:: + +Info 2 [00:00:25.000] Search path: /packages/babel-loader/src +Info 3 [00:00:26.000] For info: /packages/babel-loader/src/index.ts :: Config file name: /packages/babel-loader/tsconfig.json +Info 4 [00:00:27.000] Creating configuration project /packages/babel-loader/tsconfig.json +Info 5 [00:00:28.000] FileWatcher:: Added:: WatchInfo: /packages/babel-loader/tsconfig.json 2000 undefined Project: /packages/babel-loader/tsconfig.json WatchType: Config file +Info 6 [00:00:29.000] Config: /packages/babel-loader/tsconfig.json : { + "rootNames": [ + "/packages/babel-loader/src/index.ts" + ], + "options": { + "target": 5, + "module": 1, + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "/packages/babel-loader/src", + "outDir": "/packages/babel-loader/dist", + "configFilePath": "/packages/babel-loader/tsconfig.json" + }, + "projectReferences": [ + { + "path": "/packages/core", + "originalPath": "../core" + } + ] +} +Info 7 [00:00:30.000] DirectoryWatcher:: Added:: WatchInfo: /packages/babel-loader/src 1 undefined Config: /packages/babel-loader/tsconfig.json WatchType: Wild card directory +Info 8 [00:00:31.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /packages/babel-loader/src 1 undefined Config: /packages/babel-loader/tsconfig.json WatchType: Wild card directory +Info 9 [00:00:32.000] Starting updateGraphWorker: Project: /packages/babel-loader/tsconfig.json +Info 10 [00:00:33.000] Config: /packages/core/tsconfig.json : { + "rootNames": [ + "/packages/core/src/index.ts", + "/packages/core/src/loading-indicator.ts" + ], + "options": { + "target": 5, + "module": 1, + "strict": true, + "esModuleInterop": true, + "composite": true, + "rootDir": "/packages/core/src", + "outDir": "/packages/core/dist", + "configFilePath": "/packages/core/tsconfig.json" + } +} +Info 11 [00:00:34.000] FileWatcher:: Added:: WatchInfo: /packages/core/tsconfig.json 2000 undefined Project: /packages/babel-loader/tsconfig.json WatchType: Config file +Info 12 [00:00:35.000] DirectoryWatcher:: Added:: WatchInfo: /packages/core/src 1 undefined Config: /packages/core/tsconfig.json WatchType: Wild card directory +Info 13 [00:00:36.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /packages/core/src 1 undefined Config: /packages/core/tsconfig.json WatchType: Wild card directory +Info 14 [00:00:37.000] FileWatcher:: Added:: WatchInfo: /packages/core/src/index.ts 500 undefined WatchType: Closed Script info +Info 15 [00:00:38.000] FileWatcher:: Added:: WatchInfo: /packages/core/src/loading-indicator.ts 500 undefined WatchType: Closed Script info +Info 16 [00:00:39.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.es2018.full.d.ts 500 undefined Project: /packages/babel-loader/tsconfig.json WatchType: Missing file +Info 17 [00:00:40.000] DirectoryWatcher:: Added:: WatchInfo: /packages/babel-loader/node_modules/@types 1 undefined Project: /packages/babel-loader/tsconfig.json WatchType: Type roots +Info 18 [00:00:41.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /packages/babel-loader/node_modules/@types 1 undefined Project: /packages/babel-loader/tsconfig.json WatchType: Type roots +Info 19 [00:00:42.000] Finishing updateGraphWorker: Project: /packages/babel-loader/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 20 [00:00:43.000] Project '/packages/babel-loader/tsconfig.json' (Configured) +Info 21 [00:00:44.000] Files (3) + /packages/core/src/loading-indicator.ts + /packages/core/src/index.ts + /packages/babel-loader/src/index.ts + + + ../core/src/loading-indicator.ts + Imported via "./loading-indicator.js" from file '../core/src/index.ts' + ../core/src/index.ts + Imported via "../../core/src/index.js" from file 'src/index.ts' + src/index.ts + Matched by include pattern 'src' in 'tsconfig.json' + +Info 22 [00:00:45.000] ----------------------------------------------- +Info 23 [00:00:46.000] Search path: /packages/babel-loader +Info 24 [00:00:47.000] For info: /packages/babel-loader/tsconfig.json :: No config files found. +Info 25 [00:00:48.000] Project '/packages/babel-loader/tsconfig.json' (Configured) +Info 25 [00:00:49.000] Files (3) + +Info 25 [00:00:50.000] ----------------------------------------------- +Info 25 [00:00:51.000] Open files: +Info 25 [00:00:52.000] FileName: /packages/babel-loader/src/index.ts ProjectRootPath: undefined +Info 25 [00:00:53.000] Projects: /packages/babel-loader/tsconfig.json +After request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/index.ts: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 25 [00:00:54.000] response: + { + "response": true, + "responseRequired": true + } +Info 26 [00:00:55.000] request: + { + "command": "updateOpen", + "arguments": { + "openFiles": [ + { + "file": "/packages/core/src/index.ts", + "fileContent": "\nimport { Bar } from \"./loading-indicator.js\";\nexport type Foo = {};\nconst bar: Bar = {\n prop: 0\n}\n" + } + ] + }, + "seq": 2, + "type": "request" + } +Before request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/index.ts: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 27 [00:00:56.000] FileWatcher:: Close:: WatchInfo: /packages/core/src/index.ts 500 undefined WatchType: Closed Script info +Info 28 [00:00:57.000] Search path: /packages/core/src +Info 29 [00:00:58.000] For info: /packages/core/src/index.ts :: Config file name: /packages/core/tsconfig.json +Info 30 [00:00:59.000] Creating configuration project /packages/core/tsconfig.json +Info 31 [00:01:00.000] Starting updateGraphWorker: Project: /packages/core/tsconfig.json +Info 32 [00:01:01.000] FileWatcher:: Added:: WatchInfo: /a/lib/lib.es2018.full.d.ts 500 undefined Project: /packages/core/tsconfig.json WatchType: Missing file +Info 33 [00:01:02.000] DirectoryWatcher:: Added:: WatchInfo: /packages/core/node_modules/@types 1 undefined Project: /packages/core/tsconfig.json WatchType: Type roots +Info 34 [00:01:03.000] Elapsed:: *ms DirectoryWatcher:: Added:: WatchInfo: /packages/core/node_modules/@types 1 undefined Project: /packages/core/tsconfig.json WatchType: Type roots +Info 35 [00:01:04.000] Finishing updateGraphWorker: Project: /packages/core/tsconfig.json Version: 1 structureChanged: true structureIsReused:: Not Elapsed:: *ms +Info 36 [00:01:05.000] Project '/packages/core/tsconfig.json' (Configured) +Info 37 [00:01:06.000] Files (2) + /packages/core/src/loading-indicator.ts + /packages/core/src/index.ts + + + src/loading-indicator.ts + Imported via "./loading-indicator.js" from file 'src/index.ts' + Matched by include pattern './src' in 'tsconfig.json' + src/index.ts + Matched by include pattern './src' in 'tsconfig.json' + +Info 38 [00:01:07.000] ----------------------------------------------- +Info 39 [00:01:08.000] Search path: /packages/core +Info 40 [00:01:09.000] For info: /packages/core/tsconfig.json :: No config files found. +Info 41 [00:01:10.000] Project '/packages/babel-loader/tsconfig.json' (Configured) +Info 41 [00:01:11.000] Files (3) + +Info 41 [00:01:12.000] ----------------------------------------------- +Info 41 [00:01:13.000] Project '/packages/core/tsconfig.json' (Configured) +Info 41 [00:01:14.000] Files (2) + +Info 41 [00:01:15.000] ----------------------------------------------- +Info 41 [00:01:16.000] Open files: +Info 41 [00:01:17.000] FileName: /packages/babel-loader/src/index.ts ProjectRootPath: undefined +Info 41 [00:01:18.000] Projects: /packages/babel-loader/tsconfig.json +Info 41 [00:01:19.000] FileName: /packages/core/src/index.ts ProjectRootPath: undefined +Info 41 [00:01:20.000] Projects: /packages/babel-loader/tsconfig.json,/packages/core/tsconfig.json +After request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} +/packages/core/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 41 [00:01:21.000] response: + { + "response": true, + "responseRequired": true + } +Info 42 [00:01:22.000] request: + { + "command": "updateOpen", + "arguments": { + "changedFiles": [ + { + "fileName": "/packages/babel-loader/src/index.ts", + "textChanges": [ + { + "start": { + "line": 1, + "offset": 26 + }, + "end": { + "line": 1, + "offset": 26 + }, + "newText": "// comment" + } + ] + } + ] + }, + "seq": 3, + "type": "request" + } +Before request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} +/packages/core/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +After request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} +/packages/core/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 43 [00:01:23.000] response: + { + "response": true, + "responseRequired": true + } +Info 44 [00:01:24.000] request: + { + "command": "references", + "arguments": { + "file": "/packages/core/src/index.ts", + "line": 5, + "offset": 5 + }, + "seq": 4, + "type": "request" + } +Before request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} +/packages/core/node_modules/@types: + {"pollingInterval":500} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 45 [00:01:25.000] Finding references to /packages/core/src/index.ts position 92 in project /packages/core/tsconfig.json +Info 46 [00:01:26.000] Starting updateGraphWorker: Project: /packages/babel-loader/tsconfig.json +Info 47 [00:01:27.000] Finishing updateGraphWorker: Project: /packages/babel-loader/tsconfig.json Version: 2 structureChanged: true structureIsReused:: SafeModules Elapsed:: *ms +Info 48 [00:01:28.000] Project '/packages/babel-loader/tsconfig.json' (Configured) +Info 49 [00:01:29.000] Files (1) + /packages/babel-loader/src/index.ts + + + src/index.ts + Matched by include pattern 'src' in 'tsconfig.json' + +Info 50 [00:01:30.000] ----------------------------------------------- +Info 51 [00:01:31.000] FileWatcher:: Added:: WatchInfo: /packages/core/dist/loading-indicator.d.ts 2000 undefined Project: /packages/core/tsconfig.json WatchType: Missing generated file +After request + +PolledWatches:: +/a/lib/lib.es2018.full.d.ts: + {"pollingInterval":500} +/packages/babel-loader/node_modules/@types: + {"pollingInterval":500} +/packages/core/node_modules/@types: + {"pollingInterval":500} +/packages/core/dist/loading-indicator.d.ts: + {"pollingInterval":2000} + +FsWatches:: +/packages/babel-loader/tsconfig.json: + {} +/packages/core/tsconfig.json: + {} +/packages/core/src/loading-indicator.ts: + {} + +FsWatchesRecursive:: +/packages/babel-loader/src: + {} +/packages/core/src: + {} + +Info 52 [00:01:32.000] response: + { + "response": { + "refs": [ + { + "file": "/packages/core/src/loading-indicator.ts", + "start": { + "line": 3, + "offset": 5 + }, + "end": { + "line": 3, + "offset": 9 + }, + "contextStart": { + "line": 3, + "offset": 5 + }, + "contextEnd": { + "line": 3, + "offset": 18 + }, + "lineText": " prop: number;", + "isWriteAccess": false, + "isDefinition": false + }, + { + "file": "/packages/core/src/loading-indicator.ts", + "start": { + "line": 6, + "offset": 5 + }, + "end": { + "line": 6, + "offset": 9 + }, + "contextStart": { + "line": 6, + "offset": 5 + }, + "contextEnd": { + "line": 6, + "offset": 12 + }, + "lineText": " prop: 1", + "isWriteAccess": true, + "isDefinition": false + }, + { + "file": "/packages/core/src/index.ts", + "start": { + "line": 5, + "offset": 5 + }, + "end": { + "line": 5, + "offset": 9 + }, + "contextStart": { + "line": 5, + "offset": 5 + }, + "contextEnd": { + "line": 5, + "offset": 12 + }, + "lineText": " prop: 0", + "isWriteAccess": true, + "isDefinition": true + } + ], + "symbolName": "prop", + "symbolStartOffset": 5, + "symbolDisplayString": "(property) Bar.prop: number" + }, + "responseRequired": true + } \ No newline at end of file