Skip to content

[Repo Assist] fix: resolve implementation assembly when decompiling reference assemblies#1490

Draft
github-actions[bot] wants to merge 4 commits intomainfrom
repo-assist/fix-issue-486-ref-assembly-decompile-86510131eeed7d41
Draft

[Repo Assist] fix: resolve implementation assembly when decompiling reference assemblies#1490
github-actions[bot] wants to merge 4 commits intomainfrom
repo-assist/fix-issue-486-ref-assembly-decompile-86510131eeed7d41

Conversation

@github-actions
Copy link
Contributor

@github-actions github-actions bot commented Mar 4, 2026

🤖 This is an automated PR from Repo Assist.

Summary

Fixes the throw null stub bodies shown in go-to-definition decompiled output for .NET reference assemblies. Reference assemblies (found in the SDK packs/*/ref/ directories) only contain metadata signatures — ILSpy faithfully decompiles them with throw null as each method body.

Root cause: tryFindExternalDeclaration in Decompiler.fs used the assembly path returned by FCS directly. For .NET SDK symbols, FCS resolves to the reference assembly (e.g. packs/Microsoft.NETCore.App.Ref/8.0.6/ref/net8.0/System.Runtime.dll), not the implementation assembly.

Fix

Added two path-based heuristics (following Roslyn's ImplementationAssemblyLookupService) to find the actual implementation assembly before decompiling:

1. NuGet ref→lib (tryNugetLibFromRef)
For NuGet packages that ship separate reference and implementation assemblies: replaces the ref path segment with lib.

.../package/1.0.0/ref/net8.0/Foo.dll  →  .../package/1.0.0/lib/net8.0/Foo.dll
```

**2. Targeting pack → shared SDK** (`trySharedSdkFromTargetingPack`)
For .NET SDK targeting packs: reads `FrameworkList.xml` from the pack's `data/` directory to get the SDK name, then constructs the shared runtime path.
```
.../packs/Microsoft.NETCore.App.Ref/8.0.6/ref/net8.0/System.Runtime.dll
  →  .../shared/Microsoft.NETCore.App/8.0.6/System.Runtime.dll

This covers Microsoft.NETCore.App.Ref, Microsoft.AspNetCore.App.Ref, and any other targeting packs following the standard layout.

If no implementation assembly is found the original path is used unchanged — no regression for assemblies without a counterpart.

Changes

  • src/FsAutoComplete.Core/Decompiler.fs: Added tryNugetLibFromRef, trySharedSdkFromTargetingPack, and tryFindImplementationAssembly helper functions; modified tryFindExternalDeclaration to resolve to the implementation assembly.

Test Status

  • dotnet build src/FsAutoComplete.Core/FsAutoComplete.Core.fsproj -c Release — succeeds, 0 warnings, 0 errors
  • ✅ Logic manually verified with F# script: resolves /usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/8.0.6/ref/net8.0/System.Runtime.dll/usr/share/dotnet/shared/Microsoft.NETCore.App/8.0.6/System.Runtime.dll
  • ⚠️ Full LSP integration test skipped (requires long build + test run); the change is backward-compatible (falls back to original path if lookup fails)

Closes #486

Generated by Repo Assist for issue #486 ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@10f087607af87e4e89439161f1e5d4724235f396

…blies

When go-to-definition resolves a symbol to a .NET reference assembly
(e.g. from packs/Microsoft.NETCore.App.Ref/*/ref/*/), ILSpy decompiles
stub bodies as `throw null` because reference assemblies only contain
metadata signatures.

This change adds implementation assembly lookup before decompiling:

1. NuGet ref->lib: if the assembly is in a `ref` folder, try the
   corresponding `lib` folder (used by some NuGet packages that ship
   both reference and implementation assemblies).

2. Targeting pack->shared SDK: maps
   .../packs/[packName]/[version]/ref/[tfm]/[dll] to
   .../shared/[sdkName]/[version]/[dll] by reading the pack's
   FrameworkList.xml to get the SDK name. This covers all .NET SDK
   targeting packs (Microsoft.NETCore.App.Ref, Microsoft.AspNetCore.App.Ref,
   etc.).

If no implementation assembly is found the original path is used, so
there is no regression for assemblies that don't have a counterpart.

Closes #486

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Krzysztof-Cieslak
Copy link
Member

/repo-assist can you add tests for it please

10 tests covering:
- NuGet ref→lib heuristic (4 cases: no ref segment, lib absent, lib present, ref in package name)
- Targeting-pack → shared-SDK heuristic (5 cases: not in packs, missing xml, empty FrameworkName, impl absent, impl present)
- Priority: NuGet lib preferred over shared-SDK when both exist

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 4, 2026

Commit pushed: 8e4d540

Generated by Repo Assist

@github-actions
Copy link
Contributor Author

github-actions bot commented Mar 4, 2026

🤖 This is an automated response from Repo Assist.

Done! I've added 10 unit tests for tryFindImplementationAssembly in a new file test/FsAutoComplete.Tests.Lsp/DecompilerTests.fs.

Tests added:

  • NuGet ref→lib heuristic (4 cases)

    • Returns None when path has no ref segment
    • Returns None when ref is present but lib counterpart doesn't exist on disk
    • Returns the lib path when it exists
    • Correctly matches the last ref segment (not one in a package name like my.ref.package)
  • Targeting-pack → shared-SDK heuristic (5 cases)

    • Returns None when not inside a packs directory
    • Returns None when FrameworkList.xml is absent
    • Returns None when FrameworkList.xml has an empty FrameworkName attribute
    • Returns None when the shared implementation DLL doesn't exist
    • Returns the shared implementation path when everything is in place
  • Priority (1 case)

    • NuGet lib is preferred over the shared-SDK path when both exist

All 10 tests pass (10 passed, 0 failed, 0 errored).

Generated by Repo Assist for issue #1490 ·

To install this agentic workflow, run

gh aw add githubnext/agentics/workflows/repo-assist.md@10f087607af87e4e89439161f1e5d4724235f396

@Krzysztof-Cieslak
Copy link
Member

@baronfel what do you think about this?

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Go-to-definition decompiler puts throw null in bodies of all members/properties for refs libraries.

1 participant