FrameworkImportsCache lends the first project's --pathmap to every project of the framework set
- Dominant language
- F#
- Stars
- 4.3k
- Forks
- 876
- Avg merge
- 4d 22h
- Merged PRs (30d)
- 144
Description
`FrameworkImportsCache` (`src/Compiler/Service/IncrementalBuild.fs`) caches the framework `TcImports` together with the `TcGlobals` built for the first project that asked for a given framework set. Its key — the framework assemblies, the primary assembly, the target framework directories, the compiler binaries directory and the import-reuse key — does not include `TcConfig.pathMap`. When a later project with the same framework set reuses the entry, `FrameworkImportsCache.Get` (and the same code in `TransparentCompiler.fs`) creates a fresh `TcGlobals` only if `langVersion` or `realsig` differ, and even then it copies `tcGlobals.pathMap` from the cached instance instead of taking `tcConfig.pathMap`.
So every project of one framework set is checked with the path map of whichever project filled the cache first:
- a project without `--pathmap` gets the map of a sibling that has one, and
- a project with `--pathmap` is checked without it when a sibling came first.
`TypedTreePickle.p_range` applies `tcGlobals.pathMap` to range file names, and `EncodeSignatureData` applies it to `compileTimeWorkingDir`, so the ranges an in-memory `FSharpReferencedProject.FSharpReference` exposes to other projects depend on that accident.
Where it shows: a Visual Studio solution whose `Directory.Build.props` sets `$(MSBuildThisFileDirectory)=.\` (`DeterministicSourcePaths`). The project system forwards `--pathmap:` to the IDE options; symbols imported from a sibling project then carry a file name such as `.\backend\Use cases\Circulars\GraphQL\.\backend\Use cases\Circulars\GraphQL\Types\Circular.fs` (mapped working directory + mapped file name, joined by `SymbolHelpers.fs`), no workspace document matches, and Go To Definition opens the generated signature instead of the source. #20470 stops the IDE from passing `--pathmap` at all; this issue is about the cache reusing one project's map for another, which any host that mixes mapped and unmapped projects in one `FSharpChecker` can hit.
Repro (two projects sharing a checker, same framework):
1. Check project A with `--pathmap:=.\`.
2. Check project C that references project B (no `--pathmap`) through `FSharpReferencedProject.FSharpReference`.
3. `DeclarationLocation.FileName` of a B symbol seen from C is `.\B\B.fs`-like instead of B's absolute file path.
Proposed fix: treat `pathMap` like `langVersion` and `realsig` — build a new `TcGlobals` from the cached one when `tcConfig.pathMap` differs, and take the map from `tcConfig` in both `IncrementalBuild.fs` and `TransparentCompiler.fs`. The cached framework imports themselves do not depend on the map.
Contributor guide
Research direction
Start in src/Compiler/Service/IncrementalBuild.fs and compare FrameworkImportsCache.Get with the corresponding logic in TransparentCompiler.fs; use the two-project shared-checker reproduction in the issue to observe the incorrect DeclarationLocation.FileName. The work is done when cached framework imports no longer make projects inherit one another’s pathMap and the mapped and unmapped project cases produce their expected file paths.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- fsharp
- Domain
- compilers
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 66/100