Nimblesite / Nimblesite/SharpLsp
Closure dedup is case-insensitive on case-sensitive filesystems, silently dropping includes
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 132
- Forks
- 5
- Avg merge
- 6h 24m
- Merged PRs (30d)
- 27
Description
DocumentClosure.ExpansionState.Visited is built with StringComparer.OrdinalIgnoreCase:
That is correct on Windows and on case-insensitive macOS volumes, but wrong on Linux and case-sensitive APFS/HFS+ volumes, where Helpers/Foo.cs and Helpers/foo.cs are two distinct files.
Failure
A file-based app that includes both:
#:include Helpers/Foo.cs
#:include Helpers/foo.cs
visits the first, then Visited.Add(full) returns false for the second because the comparer folds case. VisitAsync returns early via RecordBound, which only records an issue when Files.Count >= MaxFiles or depth > MaxDepth — neither holds here. So the second file is dropped from the closure with no diagnostic and no logged issue. Every symbol it declares then reports CS0246/CS0103 in the editor with nothing explaining why.
The same fold applies to the cycle guard, so a genuine two-file include graph differing only in case is misreported as a cycle.
Fix
Match the comparer to the filesystem rather than the OS. OperatingSystem.IsWindows() is the wrong axis — macOS is case-insensitive by default but can be case-sensitive per volume. Probing the containing directory once per closure expansion and caching the result is the accurate approach; keying on OperatingSystem.IsWindows() || OperatingSystem.IsMacOS() is the cheap approximation.
Whichever is chosen, RecordBound should also record an issue for the already-visited case so a dropped file is never silent.
Test
Coarse e2e in WorkspaceManagerSingleFileTests.cs, guarded so it asserts real behaviour on case-sensitive filesystems only: write Foo.cs and foo.cs declaring different types, include both, assert both types bind (no CS0246).
Found while reviewing #189. Not a regression from it — the comparer predates the closure rewrite in spirit and ships with it. Tracked separately to keep #189's scope honest.
Refs [SCRIPT-CLOSURE].
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in sidecars/SharpLsp.Sidecar.CSharp/Workspace/DocumentClosure.cs around ExpansionState.Visited and RecordBound, then inspect WorkspaceManagerSingleFileTests.cs for the coarse end-to-end coverage. Reproduce the case-sensitive filesystem scenario with Foo.cs and foo.cs, and consider the proposed filesystem probing or approximation; done means both types bind without CS0246 and a dropped already-visited file is not silent.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- csharp
- Domain
- operating-systems, testing-qa
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100