C# 15 (.NET 11 preview 5) encodes the closed modifier as
System.Runtime.CompilerServices.ClosedAttribute on the implicitly
abstract type plus CompilerFeatureRequired("ClosedClasses") on every
constructor; the BCL does not ship the attribute yet, so assemblies
declare their own copy or reference one from another assembly. The
decompiler now reconstructs the modifier from this encoding, gated on a
new C# 15 ClosedHierarchies setting. Exported projects state LangVersion
preview because the compiler does not accept 15.0 yet.
The pretty tests cover the definition side only: switch exhaustiveness
over a closed hierarchy is compile-time knowledge that leaves no trace
in the IL of consuming code.
Compiling "#dependency" test assemblies to a temp file left them
unresolvable while decompiling the main test assembly; they now land
next to the main output. With its dependency resolvable, Issue3684 no
longer needs the disambiguating base-class cast. The cross-assembly
fixture also exposed a latent NRE in RecordDecompiler when a record's
base type cannot be resolved.
Assisted-by: Claude:claude-fable-5:Claude Code
Backfills the standard MIT X11 header on hand-written files that never
got one, attributing each to its first-commit author and year from git
history. Code vendored from dotnet/runtime and Humanizr/Humanizer gets
its origin's license lines and a provenance note instead. Generated
files (Resources.Designer.cs, the version-info template), tool-managed
suppression files, and BAML test-case fixtures intentionally stay
header-less.
Assisted-by: Claude:claude-fable-5:Claude Code
LocalFunctionMethod.MemberDefinition returned the instance itself, so the
declaration of a generic local function (whose base method is an identity
specialization) and its use sites (whose base methods carry the use-site
substitutions) never compared equal, and click-highlighting could not group
them. Returning a wrapper around the unspecialized base method lets the
token writer record the same definition object for the declaration and all
use sites. Method-group references to local functions additionally need
their own lookup, because GetSymbol() does not surface a
MethodGroupResolveResult.
The token-writer tests now keep the PEFile alive for the duration of each
test: recorded references are type-system entities that lazily read from
the PE image, and formatting one in an assertion message after disposal
crashed with an AccessViolationException.
Assisted-by: Claude:claude-fable-5:Claude Code
Runtime async is a compiler feature that emits ordinary async/await (a
C# 5 construct), so reconstructing it should not require selecting C# 15.
The dedicated RuntimeAsync setting was also redundant: AsyncAwaitDecompiler
already runs the runtime-async transforms only when AsyncAwait is enabled.
Fold the behavior into the AsyncAwait setting and drop the separate toggle.
Assisted-by: Claude:claude-opus-4-8:Claude Code
Add TaskType.UnpackAnyTask and use it in ILReader.Init / ReadIL so methodReturnStackType and function.AsyncReturnType agree for runtime-async methods that return ValueTask/ValueTask<T> or any [AsyncMethodBuilder]-attributed custom task type. Previously only Task/Task<T> were unpacked, leaving AsyncReturnType=void while the IL Leave value carried the unpacked element type, which tripped the StackType assert in ExpressionBuilder.Translate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Detect MethodImplOptions.Async (0x2000) in ILReader and unpack Task/Task<T>
return types so the IL Leave value and function.AsyncReturnType match the
source signature. Add CSharp15_0 (Preview also bumped to 1500) and a
RuntimeAsync setting (default on, gated to >=CSharp15_0), expose it in the
Languages dropdown, mask the synthetic MethodImplAsync bit out of the
decompiled [MethodImpl], and add a .runtimeasync test suffix.
* ExtensionDeclaration.SymbolKind (CA1065) — was throwing
NotImplementedException; return SymbolKind.TypeDefinition to match
TypeDeclaration / DelegateDeclaration, since `extension` declarations
are type-level.
* CustomAttribute.DecodeValue (CA2002) — replace `lock(this)` on the
sealed-but-internal class with a private syncRoot field.
* PlainTextOutput (CA1001) — implement IDisposable; track an
ownsWriter flag so we only dispose the underlying TextWriter when
the parameterless constructor created its own StringWriter.
* DotNetCorePathFinder (CA1060) — move the libc realpath / free
PInvokes into a private nested NativeMethods class.
* ILSpy.ReadyToRun (CA1016) — add [assembly: AssemblyVersion("1.0.0.0")]
in Properties/AssemblyInfo.cs to match the BamlDecompiler plugin.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This improves how function pointers are decompiled.
* ExpressionBuilder::VisitLdFtn now properly constructs the calling conventions.
* FunctionPointerType::FromSignature now checks whether a modopt type affects the calling convention.