dotnet / dotnet/fsharp

FrameworkImportsCache lends the first project's --pathmap to every project of the framework set

Open
#20,474 0 comments 0 reactions 0 assignees View on GitHub
Needs-Triage
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.