Skip to content

Conversation

dyxushuai
Copy link
Contributor

Two issues here:

  1. nil value with type, can't be equal to nil in interface, https://go.dev/doc/faq#nil_error
  2. We need to always initialize the entry regardless of the ok value.

The fix:

  • Always call entry()
  • Check if the returned *diskFile is nil
  • Return nil from GetFile() if file doesn't exist (not interface wrapping nil)

Fixes #1821

@Copilot Copilot AI review requested due to automatic review settings October 6, 2025 05:52
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR fixes a nil pointer dereference issue in the snapshotFS.GetFile method when handling non-existent files. The problem occurred due to Go's interface nil semantics where a nil value with a type is not equal to nil when stored in an interface.

  • Refactored the LoadOrStore logic to always call the entry function regardless of cache hit/miss
  • Added explicit nil check for the returned file before wrapping it in an interface
  • Added comprehensive test coverage for non-existent file scenarios including cache behavior

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
internal/project/snapshotfs.go Fixed nil pointer dereference by always calling entry() and checking for nil before returning
internal/project/snapshot_test.go Added test cases to verify GetFile and ReadFile behavior with non-existent files

@dyxushuai
Copy link
Contributor Author

@microsoft-github-policy-service agree

readFiles collections.SyncMap[tspath.Path, memoizedDiskFile]
}

type memoizedDiskFile func() *diskFile
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you do:

Suggested change
type memoizedDiskFile func() FileHandle

And then change the OnceValue func to return FileHandle, that also fixes the bug without introducing other changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, sounds good, another issue is we still need to try to initialize newEntry during the first GetFile call?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, if we always need to initialize the newEntry by calling entry, then we don't need memoizedDiskFile anymore. We can just init/store it directly without any lazy mechanism.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what you mean... I just changed this line then fixed the compiler error it makes, and then it just worked.

Copy link
Contributor Author

@dyxushuai dyxushuai Oct 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if entry, ok := s.readFiles.LoadOrStore(s.toPath(fileName), newEntry); ok {
	return entry()
}
return nil

ok == false here did not exist before, but stored a newEntry, So return nil directly.

I'm not sure what you mean... I just changed this line then fixed the compiler error it makes, and then it just worked.

Of course, it works when GetFile is called twice.

  • First call: LoadOrStore stores a memoized function and returns nil
  • Second call: LoadOrStore returns the existing entry (ok=true)

However, I am not sure shaw we call entry to get the file directly in the first call?

entry, _ := s.readFiles.LoadOrStore(s.toPath(fileName), newEntry)
return entry()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter code seems correct to me. In this atomic pattern we typically discard the ok entirely

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latter code seems correct to me. In this atomic pattern we typically discard the ok entirely

Pls review it again

@dyxushuai
Copy link
Contributor Author

Pull Request Overview

This PR fixes a nil pointer dereference issue in the snapshotFS.GetFile method when handling non-existent files. The problem occurred due to Go's interface nil semantics where a nil value with a type is not equal to nil when stored in an interface.

  • Refactored the LoadOrStore logic to always call the entry function regardless of cache hit/miss
  • Added explicit nil check for the returned file before wrapping it in an interface
  • Added comprehensive test coverage for non-existent file scenarios including cache behavior

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
internal/project/snapshotfs.go Fixed nil pointer dereference by always calling entry() and checking for nil before returning
internal/project/snapshot_test.go Added test cases to verify GetFile and ReadFile behavior with non-existent files

Always can't be nil, because we use the LoadOrStore method, there will be at least one value

@jakebailey jakebailey enabled auto-merge October 6, 2025 14:34
@jakebailey jakebailey added this pull request to the merge queue Oct 6, 2025
Merged via the queue into microsoft:main with commit 59f12cf Oct 6, 2025
22 checks passed
nathanwhit added a commit to denoland/typescript-go that referenced this pull request Oct 6, 2025
* Port "Infer from annotated return type nodes before assigning contextual parameter types" (microsoft#1804)

* Port "make exported destructured discriminated union narrowing work" (microsoft#1806)

Co-authored-by: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= <[email protected]>

* Implement more of doc highlights / find all refs (microsoft#1796)

Co-authored-by: copilot-swe-agent[bot] <[email protected]>

* Port "Provide string completions for `in` keyword checks" (microsoft#1803)

* Port "Fix #61098" (microsoft#1810)

Co-authored-by: Hans Brende <[email protected]>

* Port "fix(61258): Renaming namespace with const enum doesn't update enum references" (microsoft#1811)

Co-authored-by: "Oleksandr T." <[email protected]>

* Update dependencies (microsoft#1785)

* Mark libReplacement default as false (microsoft#1812)

* Port "Deemphasize old JSX transform" (microsoft#1809)

Co-authored-by: "Sebastian \"Sebbie\" Silbermann" <[email protected]>

* Port "fix(checker): report error when using bigint as enum key" (microsoft#1814)

Co-authored-by: magic-akari <[email protected]>

* Port "explicitly disallow `using` in ambient contexts" (microsoft#1815)

Co-authored-by: =?UTF-8?q?Ren=C3=A9?= <[email protected]>

* Port 'Issue "'{0}' declarations can only be declared inside a block." for block-scoped variables in presence of parse errors' (microsoft#1816)

* Port "Avoid resolving source prop type when the target is `unknown`/`any`" (microsoft#1817)

* Port "Allow trailing commas after import attributes in `ImportType`" (microsoft#1818)

* Port "Fix BigInt literal error in ambient contexts when targeting < ES2020" (microsoft#1819)

* Port "Stop reassigning `.valueDeclaration` to avoid replacing earlier declarations with late ones" (microsoft#1813)

* Port "Keep accessors as accessors in emitted anonymous class declarations" (microsoft#1822)

* Port "Fixed crash in `hasVisibleDeclarations` related to binding elements" (microsoft#1820)

* Port "Properly disallow `yield` in bodyless arrows" (microsoft#1825)

* Port "Account for right operands & fix a weird error message for leftmost nullish literals in checkNullishCoalesceOperands" (microsoft#1805)

Co-authored-by: Chiri Vulpes <[email protected]>

* Remove JSDoc handling from the binder (microsoft#1827)

* Port "`arguments` should not be allowed in class static block" (microsoft#1828)

Co-authored-by: Zzzen <[email protected]>

* Port "check usage before declaration for decorators" (microsoft#1829)

Co-authored-by: Zzzen <[email protected]>

* Fix nil pointer dereference in snapshotFS.GetFile for non-existent files (microsoft#1830)

* Fix translation bug causing `unknown` infer-extends constraint to be emitted (microsoft#1831)

* Port "Fixed nullish coalesce operator's precedence" (microsoft#1824)

* Fixed an issue with type params being renamed incorrectly (microsoft#1833)

* Port "Add support for `import defer` proposal"  (microsoft#1826)

Co-authored-by: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= <[email protected]>

* Bump github/codeql-action from 3.30.5 to 3.30.6 in the github-actions group across 1 directory (microsoft#1832)

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* Port "Preserve type parameter constraint in emitted mapped types while preserving their distributivity" (microsoft#1834)

* disable create-cache ci job

* merge fixup

---------

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: Mateusz Burzyński <[email protected]>
Co-authored-by: =?UTF-8?q?Michal=20Mar=C5=A1=C3=A1lek?= <[email protected]>
Co-authored-by: Jake Bailey <[email protected]>
Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: Hans Brende <[email protected]>
Co-authored-by: "Oleksandr T." <[email protected]>
Co-authored-by: "Sebastian \"Sebbie\" Silbermann" <[email protected]>
Co-authored-by: magic-akari <[email protected]>
Co-authored-by: =?UTF-8?q?Ren=C3=A9?= <[email protected]>
Co-authored-by: Chiri Vulpes <[email protected]>
Co-authored-by: Zzzen <[email protected]>
Co-authored-by: John Xu <[email protected]>
Co-authored-by: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

textDocument/definition runtime error: invalid memory address or nil pointer dereference
2 participants