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 <PathMap>$(MSBuildThisFileDirectory)=.\</PathMap> (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):
- Check project A with
--pathmap:<dir of B>=.\.
- Check project C that references project B (no
--pathmap) through FSharpReferencedProject.FSharpReference.
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.
FrameworkImportsCache(src/Compiler/Service/IncrementalBuild.fs) caches the frameworkTcImportstogether with theTcGlobalsbuilt 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 includeTcConfig.pathMap. When a later project with the same framework set reuses the entry,FrameworkImportsCache.Get(and the same code inTransparentCompiler.fs) creates a freshTcGlobalsonly iflangVersionorrealsigdiffer, and even then it copiestcGlobals.pathMapfrom the cached instance instead of takingtcConfig.pathMap.So every project of one framework set is checked with the path map of whichever project filled the cache first:
--pathmapgets the map of a sibling that has one, and--pathmapis checked without it when a sibling came first.TypedTreePickle.p_rangeappliestcGlobals.pathMapto range file names, andEncodeSignatureDataapplies it tocompileTimeWorkingDir, so the ranges an in-memoryFSharpReferencedProject.FSharpReferenceexposes to other projects depend on that accident.Where it shows: a Visual Studio solution whose
Directory.Build.propssets<PathMap>$(MSBuildThisFileDirectory)=.\</PathMap>(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 bySymbolHelpers.fs), no workspace document matches, and Go To Definition opens the generated signature instead of the source. #20470 stops the IDE from passing--pathmapat all; this issue is about the cache reusing one project's map for another, which any host that mixes mapped and unmapped projects in oneFSharpCheckercan hit.Repro (two projects sharing a checker, same framework):
--pathmap:<dir of B>=.\.--pathmap) throughFSharpReferencedProject.FSharpReference.DeclarationLocation.FileNameof a B symbol seen from C is.\B\B.fs-like instead of B's absolute file path.Proposed fix: treat
pathMaplikelangVersionandrealsig— build a newTcGlobalsfrom the cached one whentcConfig.pathMapdiffers, and take the map fromtcConfigin bothIncrementalBuild.fsandTransparentCompiler.fs. The cached framework imports themselves do not depend on the map.