Allow overriding an assembly's target framework for reference resolution#3816
Conversation
5380600 to
a4f1a3f
Compare
|
A visual distinction between auto-detected framework and manually locked-in ones would be nice. Also, a context menu item to "Reset to auto" (only visible when manually locked-in) instead of having to go through the dialog when manually locked-in would be nice. |
There was a problem hiding this comment.
Pull request overview
Adds support for overriding an assembly’s effective Target Framework Moniker (TFM) used for reference resolution, including persistence in assembly-list XML and an ILSpy UI flow to edit/reset the override.
Changes:
- Core: introduce
LoadedAssembly.TargetFrameworkIdOverride, plus separation of detected vs effective TFM (GetDetectedTargetFrameworkIdAsync()vsGetTargetFrameworkIdAsync()). - UI: add “Set Target Framework...” context menu entry and dialog; validate/convert user input via
NuGet.Frameworks. - Tests/deps: add unit tests for conversion + override persistence/reload; promote
NuGet.Frameworksto an explicit dependency and update lock files.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| TestPlugin/packages.lock.json | Lockfile updates to reflect NuGet.Frameworks dependency changes. |
| ILSpy/Views/TargetFrameworkConverter.cs | New helper to parse/validate TFMs and convert to/from FrameworkName form. |
| ILSpy/Views/SetTargetFrameworkDialog.axaml.cs | New dialog logic (presets, validation, OK/cancel behavior). |
| ILSpy/Views/SetTargetFrameworkDialog.axaml | New dialog UI layout for setting/resetting TFM override. |
| ILSpy/TreeNodes/ReferenceFolderTreeNode.cs | References listing now prints detected vs effective TFM. |
| ILSpy/Properties/Resources.resx | Adds strings for dialog label/title and invalid-input message. |
| ILSpy/Properties/Resources.Designer.cs | Generated resource accessors for new strings. |
| ILSpy/packages.lock.json | Adds NuGet.Frameworks as direct dependency; updates lockfile graph. |
| ILSpy/ILSpy.csproj | Adds explicit PackageReference to NuGet.Frameworks. |
| ILSpy/Commands/SetTargetFrameworkContextMenuEntry.cs | New context menu entry to edit override and reload assembly while restoring selection. |
| ILSpy.Tests/Views/TargetFrameworkConverterTests.cs | Unit tests for short/long TFM conversion and invalid input rejection. |
| ILSpy.Tests/packages.lock.json | Lockfile updates for NuGet.Frameworks graph changes. |
| ILSpy.Tests/Editor/DecompilerViewTests.cs | Updates expected References-folder output to include effective TFM header. |
| ILSpy.Tests/AssemblyList/TargetFrameworkOverrideTests.cs | Tests override precedence, XML round-trip compatibility, and reload carry-over. |
| ILSpy.Tests.Windows/packages.lock.json | Lockfile updates for NuGet.Frameworks graph changes. |
| ILSpy.ReadyToRun/packages.lock.json | Lockfile updates for NuGet.Frameworks graph changes. |
| ICSharpCode.ILSpyX/LoadedAssembly.cs | Implements effective vs detected TFM APIs and adds TargetFrameworkIdOverride. |
| ICSharpCode.ILSpyX/AssemblyList.cs | Persists override in assembly-list XML and carries it across reload. |
| ICSharpCode.ILSpyCmd/packages.lock.json | Lockfile updates for NuGet.Frameworks graph changes. |
| ICSharpCode.Decompiler.Tests/packages.lock.json | Lockfile updates for NuGet.Frameworks graph changes. |
| Directory.Packages.props | Adds centrally-managed NuGet.Frameworks version. |
Files not reviewed (1)
- ILSpy/Properties/Resources.Designer.cs: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a4f1a3f to
a91e8c3
Compare
ILSpy resolves an assembly's references against the target framework it detects from the TargetFrameworkAttribute. When that attribute is missing, wrong, or the user wants to force a different framework, there was no way to hint the correct one, so references could resolve against the wrong runtime pack or framework directory. A LoadedAssembly can now carry a TargetFrameworkIdOverride that short-circuits detection (it is the single value every LoadedAssembly-based resolution path reads), is persisted in the assembly-list XML, and is carried across a reload so a runtime change re-resolves against the new framework. The "Set Target Framework" context-menu entry edits it through a dialog with a free-form text box and an always-visible list of common monikers to pick from (the app forces overlay popups, so a dropdown would be clamped inside the small dialog); input is validated and converted from the short TFM users know (net48) to the long FrameworkName form the resolver consumes (.NETFramework,Version=v4.8) via NuGet. The direct DetectTargetFrameworkId callers in the decompiler core (project export, language version) intentionally keep reading the real attribute; only reference resolution is overridden. Resurrects a 2020 prototype (branch tfmoverride) re-implemented against the current ILSpyX/Avalonia code, whose surrounding structures no longer matched. Assisted-by: Claude:claude-opus-4-8:Claude Code
a91e8c3 to
8170422
Compare
|
All review feedback is addressed as of 8170422: blank/whitespace overrides normalize to null in the setter (detection is never suppressed), the TFM doc-comments use the comma form, the Debug-menu Written by Claude (claude-opus-4-8) via Claude Code, on Siegfried's behalf. |
What
Lets the user override the target framework ILSpy resolves an assembly's references against, instead of the one detected from its
TargetFrameworkAttribute. This helps when the attribute is missing, wrong, or when the user wants to force resolution against a specific framework, so references do not resolve against the wrong runtime pack or framework directory.Resurrects a 2020 prototype (branch
tfmoverride), re-implemented against the currentICSharpCode.ILSpyX/ Avalonia code whose surrounding structures no longer matched.How
ICSharpCode.ILSpyX):LoadedAssembly.TargetFrameworkIdOverrideshort-circuitsGetTargetFrameworkIdAsync()ahead of the cached detected value, soLoadedAssembly-based resolution paths read the effective TFM. The detected value remains available separately for UI/reporting. The override is persisted as a backward-compatibleTargetFrameworkattribute on the<Assembly>element and carried acrossReloadAssembly.ILSpy): a "Set Target Framework..." context-menu entry on assembly nodes opens a dialog to edit the override, then reloads so references re-resolve and restores the tree selection. The dialog pairs a free-form text box with a list of common monikers; input is validated and converted from the short TFM users know (net48) to the longFrameworkNameform the resolver consumes (.NETFramework,Version=v4.8) via NuGet. It returnsnullfor cancel, an empty string to reset to auto-detection, and a non-empty string to set an override.Detected TargetFramework-IdandEffective TargetFramework-Id, so overrides are visible without relabeling detected metadata.NuGet.Frameworksis promoted to an explicit reference (already resolved transitively at 7.6.0 viaNuGet.Protocol); lock files regenerated.Screenshots
Set the override from the assembly context menu (no override yet, so only "Set Target Framework..." is offered):
Enter the framework as the short moniker you know (
net48); the presets list covers the common ones:After the reload, the effective TFM is bolded in the tree label and the References folder reports Detected vs Effective side by side:
While an override is active, a "Reset Target Framework" entry clears it back to auto-detection:
Scope
Only reference resolution is overridden. The direct
DetectTargetFrameworkIdcallers in the decompiler core (project export, language version) keep reading the real attribute.Tests / verification
OPENSSL_ENABLE_SHA1_SIGNATURES=1 dotnet test --project ILSpy.Tests/ILSpy.Tests.csproj --filter "FullyQualifiedName~DecompilerViewTests" --report-trx --no-restore -v:minimalTest run summary: Passed! failed: 0OPENSSL_ENABLE_SHA1_SIGNATURES=1 pwsh ./build.ps1 -Configuration Debug --no-restore /m:1Build succeeded. 0 Warning(s) 0 Error(s)net48->.NETFramework,Version=v4.8drivesGetTargetFrameworkIdAsyncafter reload, invalid input shows an inline error and keeps the dialog open, and blank clears the override.