Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persistent incorrect semantic diagnostic after large rebase or checkout: Module has no exported member #47466

Closed
MarcCelani-at opened this issue Jan 16, 2022 · 6 comments · Fixed by #48997
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Milestone

Comments

@MarcCelani-at
Copy link

MarcCelani-at commented Jan 16, 2022

Bug Report

When using tsserver in VSCode, I sometimes see persistent false error reports from diagnostics after a large rebase or checkout of the form, "Module '{}' has no exported member '{}'." The file mentioned in the error is a file that was updated in the checkout or rebase. Opening the file in question in VSCode loads the file and resolves the incorrect semantic diagnostic. Alternatively, restarting TSServer resolves the incorrect diagnostic

🔎 Search Terms

module has no exported member watch

🕗 Version & Regression Information

This has been happening as long as we can remember

🙁 Actual behavior

persistent incorrect ssemantic diagnostics

🙂 Expected behavior

No errors after a reasonable period of time

@MartinJohns
Copy link
Contributor

Isn't this fixed by #47274?

@MarcCelani-at
Copy link
Author

MarcCelani-at commented Jan 16, 2022

#47274 is not fixed. Besides, these may be separate issues, because the way that caches get invalidated for program structure is different than how source file info caches get updated.

This is interesting...

My repro steps:

I have two branches, foo and bar, which include many file changes between them, including a file, "dependenecy.tsx"

  1. Open VSCode
  2. Open a file, openfile.tsx, which imports from dependency.tsx
  3. checkout branch "bar". I see a "FileWatcher:: Triggered" log line for dependency.tsx
  4. I switch back to "foo". I see "DirectoryWatcher:: Triggered" log lines for dependency.tsx, but I do not see FileWatcher:: Triggered log line should fire for dependency.tsx. With additional logging patched in, I notice that dependency.tsx is never read.
  5. From henceforth, any branch switches back and forth between foo and bar do not trigger the watch for dependency.tsx

I did not see any FileWatcher:: Closed log lines.

@MarcCelani-at
Copy link
Author

I have a theory that the issue is this code, which does not properly handle if a file's inode changes:

TypeScript/src/compiler/sys.ts

Lines 1653 to 1659 in 1298f49

return event === "rename" &&
(!relativeName ||
relativeName === lastDirectoryPart ||
(relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) !== -1 && relativeName.lastIndexOf(lastDirectoryPartWithDirectorySeparator!) === relativeName.length - lastDirectoryPartWithDirectorySeparator!.length)) &&
!fileSystemEntryExists(fileOrDirectory, entryKind) ?
invokeCallbackAndUpdateWatcher(watchMissingFileSystemEntry) :
callback(event, relativeName);

fs.watch() does not watch a "path", it watches an inode, and the inode for a path can change. The code above does not handle this because if it discovers the file still exists after a rename, it assumes the watch it still good, when there's a very good chance that it's not.

It turns out this is really easy to reproduce with just vim. Repro steps:

  1. Open a file in VSCode
  2. tail log and wait for it to calm down
  3. Open an imported file in vim
  4. Delete an export that is used in the file opened in VSCode. Issue :w command

When you issue a :w command in vim, it atomically updates the file with an operation that will trigger a rename event.

This will trigger the bug and it will manifest one of two ways.
Case 1: FileWatcher will convince itself that it has to close, and you will never see an update for this file again if you do any subsequent writes.

Case 2: FileWatcher will trigger but will not close, but after that there will be no more watch events if you do it again.

I suspect git is sometimes (always?) performing some kind of operation like vim is with :w. Perhaps a large checkout/rebase makes a race condition more likely to occur (race between git updating the file and nodejs processing the watch and checking if the file exists).

I confirmed that my git checkout repro is changing the inode with ls -i. I suspect that the way to fix this is to look at the inode information from statSync.

@MarcCelani-at
Copy link
Author

(If I am correct, then I am quite certain this issue is different than #47274)

@OliverJAsh
Copy link
Contributor

Coming here from #44066. If I add these VS Code settings the issue disappears for me, i.e. there are no stale errors when I switch branch (using my test case in #44066):

    "typescript.tsserver.watchOptions": {
        "watchFile": "useFsEvents",
        "watchDirectory": "useFsEvents"
    },

Just mentioning it in case it's helpful information for debugging.

@MarcCelani-at
Copy link
Author

This issue is for users of FsEvents, so your suggestion doesn't really apply here. But thanks for commenting!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
5 participants