Skip to content

Name a declaration from a path-mapped assembly from its root once - #20518

Open
xperiandri wants to merge 16 commits into
dotnet:mainfrom
xperiandri:fix/pathmap-declaration-location
Open

xperiandri wants to merge 16 commits into
dotnet:mainfrom
xperiandri:fix/pathmap-declaration-location

Conversation

@xperiandri

@xperiandri xperiandri commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Go To Definition into a project built with a relative --pathmap (for example . for the repository root) opened generated metadata instead of the source: the declaration's file name repeated the compile directory, as in .\src\Domain\.\src\Domain\Types.fs. A name that is one of the assembly's own compiled files is now used as recorded.

Details
  • Under a relative path map the compiler records the compile directory, and the names of the files compiled from under it with the mapped directory at their start, so joining the two repeated it.
  • A relative #line name that a compiler before F# 10 wrote is relative to the compile directory even when it starts with it, so it is still joined. The assembly tells the two apart: a name is a compiled file when a module, type or namespace has its range in that file, or a value published through a signature has its implementation there.
  • Limits, all needing an F# 9 or older writer and a relative path map: a #line directive that precedes the header of a top-level module, type or namespace, or a #line name that is also the name of a compiled file, is read as a compiled file.

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

✅ Release notes checked


✅ Found changes and release notes in following paths:

Change path Release notes path Description
`src/Compiler` docs/release-notes/.FSharp.Compiler.Service/11.0.200.md

@github-actions github-actions Bot added the ⚠️ Affects-Design-Time Tooling check: PR touches type providers or dependency manager label Sep 11, 2026
@github-actions

This comment has been minimized.

xperiandri added a commit to xperiandri/fsharp that referenced this pull request Sep 11, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@T-Gro T-Gro left a comment •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@github-project-automation github-project-automation Bot moved this from New to In Progress in F# Compiler and Tooling Sep 11, 2026
@T-Gro T-Gro added the AI-reviewed PR reviewed by AI review council label Sep 11, 2026
@T-Gro
T-Gro self-requested a review September 11, 2026 12:45
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Sep 11, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the fix/pathmap-declaration-location branch from 7cf1ff0 to a4ae807 Compare September 11, 2026 16:16

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖🕵️ Please make the description more concise using this guidance. Keep the problem and why the change is needed prominent, using short sentences in simplified technical English. Retain necessary caveats.

@github-actions

This comment has been minimized.

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

So shouldn't' this just plain remove .pathMap from tcGlobals and move it over to tcConfig ?

@github-actions

This comment has been minimized.

xperiandri added a commit to xperiandri/fsharp that referenced this pull request Sep 26, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings September 26, 2026 01:00
@xperiandri
xperiandri force-pushed the fix/pathmap-declaration-location branch from 4b662de to 78f4c4d Compare September 26, 2026 01:00

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

A moderate relative-path regression and a duplicate release-note entry remain unresolved.

Review effort: Lite
Findings: 1 Low severity

Open (1)
What changed in this PR

Fixes F# declaration paths for path-mapped assemblies and preserves project-specific path maps in cached compiler state.

Changes:

  • Corrects mapped source-path resolution.
  • Preserves path maps across cached and transparent compilation.
  • Adds regression tests and release-note documentation.
File Summary
tests/​FSharp.Compiler.ComponentTests/​FSharpChecker/​PathMap.fs Adds path-map regression tests.
tests/​FSharp.Compiler.ComponentTests/​FSharp.Compiler.ComponentTests.fsproj Includes the new tests.
src/​Compiler/​Symbols/​SymbolHelpers.fs Adjusts declaration source-path resolution.
src/​Compiler/​Service/​TransparentCompiler.fs Preserves path maps in transparent compilation.
src/​Compiler/​Service/​IncrementalBuild.fs Preserves path maps in cached builds.
docs/​release-notes/​.FSharp.Compiler.Service/​11.0.100.md Documents the fixes; contains a duplicate entry.

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

@github-actions

Copy link
Copy Markdown
Contributor

🔍 Tooling Safety Check — Affects-Design-Time
Affects-Design-Time: Changes IDE integration executed during design-time.

Generated by PR Tooling Safety Check · gpt56 1.7M · ◷

@github-actions github-actions Bot added the AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files label Sep 28, 2026
@abonie

abonie commented Oct 1, 2026

Copy link
Copy Markdown
Member

#20518 and #20519 need consistent expectations for the coordinate system of imported filenames: a relative compilation directory does not prove the filename is already root-relative, and a matching suffix does not establish source identity.

An older DLL with #line ... "Original.fs" can require the relative compilation directory to be retained, while ordinary mapped filenames already contain that directory; default deterministic mappings can also produce rooted virtual paths rather than relative names.

Could we pin these distinctions with emitted-DLL tests asserting both the compiler’s returned location and the editor’s destination, rather than relying on in-memory references whose IDE options remove the path map?

@abonie

abonie commented Oct 1, 2026

Copy link
Copy Markdown
Member

An older DLL built with #line ... "Original.fs" and a relative path map can store Original.fs relative to a compilation directory such as .\src\Domain; the new rule discards that directory because it is not rooted, turning a previously resolvable .\src\Domain\Original.fs into Original.fs.

Perhaps retain compilation-directory qualification unless the filename is established to be mapped-root-relative, rather than using the directory’s rootedness as proof that qualification is unnecessary.

An emitted older-producer DLL regression alongside the ordinary mapped-filename case would ensure that removing duplicated directory segments does not also remove a necessary directory component.

@xperiandri

Copy link
Copy Markdown
Contributor Author

@abonie Both points are addressed in 1e21fcf.

Rule. A relative file name is now joined with the compile directory unless it already starts with that directory followed by a separator. A path map writes the files compiled from under the directory with the mapped directory at their start, so only such a name is used as is. Everything else keeps the directory, as on main, and your example resolves again: .\src\Domain + Original.fs → .\src\Domain\Original.fs. A rooted directory such as /_/src/Domain can never start a relative name, so /_/ builds behave exactly as on main. One case the rule cannot establish is a mapped name from outside the compile directory, such as a linked file under a relative map. That name is still joined, as on main.

Tests. I replaced the in-memory declaration test with one that compiles the library with fsc out of process from its own directory and references the emitted DLL. It runs with no map, with . and with /_/. For an ordinary file, it asserts what GetDeclarationLocation returns, read back through the map. A current compiler cannot emit the #line case: since F# 10 (#18699) it writes the real file for a #line directive. So the test resolves Original.fs against the emitted assembly's recorded directory, through the same fileNameOfItem step GetDeclarationLocation uses. As a control, main's rule fails the ordinary . case (src\Domain\src\Domain\Library.fs), and the previous revision fails the #line . case (it returns Original.fs).

Editor. The editor's destination is decided in #20519, which this PR does not touch. For /_/ builds, what it receives on Windows is C:\_\src\Domain\Library.fs, because a range turns every rooted name into a full path.

@xperiandri
xperiandri force-pushed the fix/pathmap-declaration-location branch from 1e21fcf to 5586d58 Compare October 2, 2026 16:03
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 2, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@T-Gro T-Gro left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤖🕵️ AI review — verify independently.

xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 19, 2026
…t the multi-target tests

Brings the fixes pushed to dotnet#20518 and dotnet#20483 onto the integration branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 19, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 19, 2026
…t the multi-target tests

Brings the fixes pushed to dotnet#20518 and dotnet#20483 onto the integration branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 19, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 23, 2026
…t the multi-target tests

Brings the fixes pushed to dotnet#20518 and dotnet#20483 onto the integration branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 23, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 23, 2026
…t the multi-target tests

Brings the fixes pushed to dotnet#20518 and dotnet#20483 onto the integration branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 23, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri and others added 15 commits October 29, 2026 20:32
…ing project

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A build that maps its source paths - as DeterministicSourcePaths does - maps the
directory it compiled in along with the file names of its ranges, so the two
reach the same root and neither records what that root is. `fileNameOfItem`
joined them anyway, on the assumption that the directory is absolute and the
file name relative to it, and the declaration of a symbol imported from such an
assembly came back naming the directory twice. Nothing matches a path like that,
so Go To Definition reported the symbol as external and opened generated
metadata instead of its source.

Join only when the directory is rooted. When it is not, the file name already
reaches the root on its own and is as complete as the metadata can make it.

The IDE drops the path map from the options it builds, so this is reached
through an assembly on disk - which is where a path map is normally applied, and
which no change to those options can rewrite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On Linux and macOS a backslash is part of a file name, so a map to .\ left the
declaration's name unresolvable against the root there.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Linking the note added a copy of it instead of editing the first, and `main` has since
opened 11.0.200 for SDK 11.0.200, leaving 11.0.100 shipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A relative name in metadata is relative to the directory the assembly was
compiled in, as the #line names that compilers before F# 10 wrote are. A path
map writes the files compiled from under that directory with the mapped
directory at their start, so only such a name is used as is; the directory
being relative did not prove a name was mapped.

The in-memory test gives way to one that compiles the library out of process
from its own directory and references the emitted assembly, with no map, a
relative one and the rooted /_/ one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing project

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`main` opened 11.0.200 for SDK 11.0.200; 11.0.100 has shipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…lled the cache

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The test's arguments are joined into one command line. On .NET Framework the
default references come from the targeting pack under "Program Files (x86)",
so each was split at its spaces and fsc stopped with FS2020.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Compilers before F# 10 recorded what follows a #line directive under the name
the directive gives, relative to the compile directory. Under a relative path
map such a name can start with the mapped directory exactly as the name of a
compiled file does, and was then taken for one.

A name is now used as written only if the item also lies in the file its
outermost module or type is declared in; a #line directive leaves the two in
different files. A module read from a signature file gives no such evidence,
since only values record where their implementation is.

PathMapFSharp9.dll is real F# 9 output (FSharp.Compiler.Service 43.9.300).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A name recorded under a relative --pathmap already starts with the compile directory, so joining the two repeated it. A relative #line name that a compiler before F# 10 wrote is relative to that directory even when it starts with it. The assembly tells them apart: a name is a compiled file when a module, type or namespace has its range in that file, or a value published through a signature has its implementation there.

This replaces the walk from the item to its outermost entity, which missed custom operations, named arguments and members of types declared directly in a namespace. The tests now cover a custom operation, a namespace whose last fragment holds no type, and a value of a signature'd module that sits in a nested module.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Replaying the branch onto main left some files short of the branch's own content; this restores it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri force-pushed the fix/pathmap-declaration-location branch from 2599ec4 to 925ab17 Compare October 29, 2026 22:03
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 29, 2026
…t the multi-target tests

Brings the fixes pushed to dotnet#20518 and dotnet#20483 onto the integration branch.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
xperiandri added a commit to xperiandri/fsharp that referenced this pull request Oct 29, 2026
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Build Failure Analysis

Summary - All 16 Windows build legs fail while compiling FSharp.Compiler.ComponentTests for .NET Framework because a newly added test calls an unavailable string.Contains(string, StringComparison) overload; the 16 companion legs compile successfully.

Root cause: .NET Framework-incompatible String.Contains overload

The failing binlogs consistently report FS0001 from CoreCompile in FSharp.Compiler.ComponentTests.fsproj. The call passes (name, StringComparison.Ordinal), but the Desktop/.NET Framework BCL only exposes the one-argument Contains overload, so F# treats the pair as a tuple where a string is expected. The repository's cross-target convention is to use IndexOf(name, StringComparison.Ordinal) >= 0 for this case.

Affected errors

Fix attempt - blocked

Delivery - none

The minimal supported change is to replace line.Contains(name, System.StringComparison.Ordinal) with line.IndexOf(name, System.StringComparison.Ordinal) >= 0. No code or patch was published because the exact failing net472 target could not be validated on this Linux runner: the .NET Framework 4.7.2 reference assemblies are unavailable, and acquiring the official reference-assembly package was blocked. This is a contributor-fork PR, so an unvalidated automated push would not be permitted in any case.

Checks - Before change: ./eng/common/dotnet.sh build tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj -c Release -p:TargetFramework=net472 -p:TargetFrameworks=net472 -p:FrameworkPathOverride=/tmp/gh-aw/agent/net472/package/build/.NETFramework/v4.7.2 --no-restore exited 1 with MSB3644 because the .NET Framework 4.7.2 reference assemblies were not present. No after-change command was run and the source was left unchanged. Remaining build diagnostic: the same FS0001 at PathMap.fs:138 in every failing leg.

Build evidence

Azure build 1655081 contains 32 binlogs. Attempts 5_0 through 20_0 all fail in FSharp.Compiler.ComponentTests.fsproj with the identical FS0001; attempts 5_1 through 20_1 report successful builds with zero errors. The failed project chain is FSharp.Compiler.ComponentTests.fsproj → solution → Build.proj; no independent second root cause appears.

The analyzed PR remained open at head 925ab1795f6144c1137bc6c47cf3e05b1b8337fc, and merge revision 8f73185bb12ba536bcecfc7869bbfd1b553316ca still records that head merged into base fbc9be3aa8159670bbb660c3b7b7e22368f4e279 immediately before publication. Workflow run: https://github.com/dotnet/fsharp/actions/runs/40977960403

Structured data:

{
  "artifact_kind": "analysis",
  "fix_delivery": "none",
  "fix_status": "blocked",
  "workflow_artifact": "build-failure-analysis"
}

Warning

Firewall blocked 6 domains

The following domains were blocked by the firewall during workflow execution:

  • builds.dotnet.microsoft.com
  • ci.dot.net
  • data.nuget.org
  • h6tvsblobprodcus346.vsblob.vsassets.io
  • pkgs.dev.azure.com
  • southcentralus0.in.applicationinsights.azure.com

To allow these domains, add them to the network.allowed list in your workflow frontmatter:

network:
  allowed:
    - defaults
    - "builds.dotnet.microsoft.com"
    - "ci.dot.net"
    - "data.nuget.org"
    - "h6tvsblobprodcus346.vsblob.vsassets.io"
    - "pkgs.dev.azure.com"
    - "southcentralus0.in.applicationinsights.azure.com"

See Network Configuration for more information.

Automated content by GitHub Copilot. Generated by the Build Failure Analysis workflow. · copilot · gpt56 · 217.6 AIC · ⌖ 9.12 AIC · ⊞ 21.3K | [History]( · ◷)

…, absent on net472

The Desktop legs could not compile FSharp.Compiler.ComponentTests: the .NET Framework
BCL has no `String.Contains(string, StringComparison)`, so the arguments were read as
a tuple (FS0001 in PathMap.fs). `IndexOf(name, StringComparison.Ordinal) >= 0` exists
on every target.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xperiandri
xperiandri requested a review from T-Gro October 30, 2026 20:40

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚠️ Affects-Design-Time Tooling check: PR touches type providers or dependency manager AI-reviewed PR reviewed by AI review council AI-Tooling-Check-Scanned-Clean Tooling check: diff analyzed, no interesting infrastructure files

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

4 participants