Commit Graph
447 Commits
Author SHA1 Message Date
Siegfried Pammer 2291586659 Classify C# 14 user-defined compound assignment operators in the type system
C# 14 lets a type declare "void operator +=(T)" and friends: void-returning
instance methods under the op_*Assignment metadata names. Only that exact shape
becomes SymbolKind.Operator, and only while the new setting is on, because other
languages use the same names for unrelated methods: F# mangles
"static member (+=)" to a static, value-returning op_AdditionAssignment, and
C++/CLI emits value-returning instance operators. Those, and every method when
the setting is off, stay plain methods with a surfaced [SpecialName].

Assisted-by: Claude:claude-opus-5:Claude Code
2026-08-29 09:37:40 +02:00
Siegfried Pammer 471c3b1776 Move the remaining ResolveResult subclasses into the Semantics namespace
The resolve-result hierarchy was split between ICSharpCode.Decompiler.Semantics
and ICSharpCode.Decompiler.CSharp.Resolver, so consumers had to know which half
a given result came from and import both namespaces. All subclasses now live
next to their base class; MethodListWithDeclaringType follows the method group
it describes, and ILVariableResolveResult gets its own file instead of sitting
among the syntax-tree annotation helpers.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-15 11:03:11 +02:00
Siegfried Pammer 48fcf0313e Move UsingScope and CSharpTypeResolveContext into the type system
Both types are consumed well outside the C# output layer - DecompileRun
carries the using scope, and the IL transforms build a resolve context from
it - so living in ICSharpCode.Decompiler.CSharp.TypeSystem misrepresented
where they belong and forced a C#-specific namespace import on every
consumer.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-15 11:03:10 +02:00
Sebastien LebretonandCopilot f7cf85e263 Remove obsolete lifetime annotation fields
ScopedKind is now the authoritative lifetime representation, so retaining
the preview-era boolean fields would duplicate state. Keep the current
ScopedRef compatibility property and group the new metadata attributes
with the other C# 11 attributes.

Assisted-by: Copilot:gpt-5.6-sol:GitHub Copilot CLI
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 86d2918e-5a24-48b4-9a86-41d331ec3720
2026-08-14 15:49:25 +02:00
Sebastien LebretonandCopilot 73c3283efa Model scoped parameter lifetimes
ScopedRefAttribute only records explicit syntax. Effective lifetime also
depends on UnscopedRefAttribute, params collections, out parameters, and
the defining module's RefSafetyRules version. Model those distinctions in
the type system without changing decompiler output.

Assisted-by: Copilot:gpt-5.6-sol:GitHub Copilot CLI
Co-authored-by: Copilot <[email protected]>
Copilot-Session: 86d2918e-5a24-48b4-9a86-41d331ec3720
2026-08-14 15:49:25 +02:00
Siegfried Pammer 2efd871abc Fix #3451: qualify an explicit implementation as the type implements it
Tuple element names and nullability are not part of a type's identity, so an
interface resolved through one of its members carries neither. Naming an
explicit implementation from that type produced `void I<(int, int)>.M()` on a
type declared as `I<(int A, int B)>`, which the C# compiler rejects outright
with CS0540 - the decompiled source did not build. The nullable case was
already recorded as a TODO in the NullableRefTypes fixture, where the mismatch
costs a CS8643 warning rather than an error.

The implementing type's base-type list is the only place those annotations are
recorded, so the qualifier is looked up there. Three call sites derived it
independently - the AST builder for all five member kinds, the ambience used
for tooltips and tree labels, and the forwarders synthesized for MethodImpls -
so they now share one helper rather than repeating the rule twice more.

Matching while ignoring tuple names and nullability cannot be ambiguous:
implementing two interfaces that differ only in those is itself an error
(CS8140, CS8645).

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-11 06:21:29 +02:00
Siegfried Pammer a216586236 Require System.ValueTuple to be a struct to be tuple compatible
C# only accepts System.ValueTuple as a tuple when it is a struct, so a class of
that name is an unrelated type and rendering it with tuple syntax describes it
as something it is not. It also made a tuple appear to contain itself, which no
struct can, and the deconstruction transform then registered the same variable
as a node of its tuple tree twice and threw ArgumentException, failing the whole
method instead of leaving the statements alone.

The check has accepted classes since tuples were added to the type system,
alongside a name comparison against "ValueType" that was corrected later.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-07 16:19:00 +02:00
Siegfried Pammer 527dcb732b Fix TupleType.FromUnderlyingType crash on non-tuple input
GetTupleElementTypes returns a default ImmutableArray when the type is
not tuple-compatible, so reading Length threw NullReferenceException
instead of taking the documented return-null path.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-07 16:19:00 +02:00
Siegfried Pammer a1726a9dbf Fix #3938: nested-type members must not inherit ExtensionInfo
The .NET 10 BCL ships static [Extension] classes that contain ordinary
nested types (e.g. XDocumentExtensions.XDocumentNavigable). Decompiling
such a nested type's member in isolation resolved the enclosing
container's ExtensionInfo, and DecompileBody then dereferenced the
missing extension-member mapping. A container without any extension
blocks now reports no ExtensionInfo at all, and ResolveExtensionInfo
applies a container's info only to members that actually belong to one
of its extension blocks.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-31 13:50:00 +02:00
Siegfried Pammer e22851bd66 Remove the now-unused IMemberReference interface
IdStringMemberReference was its only implementation and left with the
ID string grammar parser; unlike the NRefactory-era type system, IMember
does not extend IMemberReference here, so nothing in the library
produces or consumes it anymore.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-30 08:35:07 +02:00
Siegfried Pammer 832090c08d Emit parameterized-property accessors as ordinary methods
C# cannot declare a named property with parameters: only the type's
default member gets indexer syntax, and reusing it via [IndexerName]
collapses for types with several differently-named indexed properties,
static properties, or explicit interface implementations. Emitting the
accessors as ordinary methods is the only fully general compilable
form, matches how C# consumes such properties (Roslyn exposes the
accessors of properties it cannot bind as regular methods, the same
pattern C# 14 made user-facing for extension-member disambiguation),
and round-trips call sites to identical IL. Call sites already lower
to direct accessor calls.

The property-level attributes are kept on the first accessor under the
'property:' attribute target: it is not valid on methods, so csc emits
nothing for it (CS0657 warning) and recompilation neither loses the
attributes from the source nor misapplies them to the accessor. A
comment on the first accessor documents the deliberate deviation.
Visual Studio's metadata-as-source view drops such properties'
attributes entirely.

The assembly tree and tooltips are unaffected: they keep rendering the
property node with its parameter list. Known limitation, inherent to
any C# projection: recompiling the output produces plain methods, so
VB.NET consumers of the recompiled assembly lose property syntax.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-27 16:28:34 +02:00
Siegfried Pammer 1c010a828e Give dynamic member accesses a symbol for hover tooltips
Dynamic member accesses and invocations carried only the member name
(DynamicMemberResolveResult / DynamicInvocationResolveResult), so
GetSymbol returned null and the editor emitted no reference or hover.

Synthesize a member on the target type - a dynamic field for a member
access, a dynamic-returning method for a member invocation - named after
the accessed member and typed from the callsite delegate: each argument
uses its recorded compile-time type when the binder set one (statically
typed or constant arguments), dynamic otherwise, and the declaring type
comes from the receiver's argument info. Route these through GetSymbol;
TextTokenWriter and the hover renderer already turn an IEntity into a
tooltip. The synthesized members have no metadata token, so they render
a signature on hover but are not navigation targets.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-23 06:32:02 +02:00
Christoph Wille 2e617a38c0 Report deduplicated reference count, deflake EventSource schema tests
TypeSystemInitStop now reports the size of the final reference set passed
to Init() instead of the raw resolve count, which included same-name
duplicates that the version dedup later drops. The schema smoke tests
asserted exact global event counts, but the providers are process-wide
and the decompiler fixtures run in parallel, so unrelated decompilations
could inflate the counts; every string payload now carries a unique
marker (and the string-less snapshot events a sentinel count) that the
assertions filter on.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-22 21:10:41 +02:00
Christoph Wille c8474419c4 Redesign DecompilerEventSource with Start/Stop tracing and keyword gating
The events introduced in #2519 timed only the five per-entity DoDecompile
overloads, allocated a Stopwatch and the member's FullName even when no
trace session was attached, and reported whole milliseconds, which rounds
almost every member to zero. Flat one-shot events also gave PerfView and
dotnet-trace no way to show durations or nesting, and the actually
expensive stages (type system initialization, assembly resolution probing,
the IL/AST transform pipelines, whole-project decompilation) were not
instrumented at all.

Start/Stop event pairs let trace viewers derive duration and nesting from
event timestamps, keywords let a session enable only the areas of
interest, and every call site is gated on IsEnabled() so tracing costs a
branch when disabled. Per-transform events are Verbose because of their
volume; unlike the STEP/Stepper mechanism they work in Release builds.
EventSource is in-box for netstandard2.0 and flows over both ETW and
EventPipe, so this stays cross-platform with no new dependency.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-22 21:10:41 +02:00
Siegfried Pammer 07d5a01d2b Add license headers to source files that lacked them
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
2026-07-05 21:09:46 +02:00
Siegfried Pammer f2b51d2c34 Fix #2078: Generic local functions not highlighted properly
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
2026-07-05 08:30:40 +02:00
Siegfried Pammer f2e17b6a6e Fix #3777: decompile runtime async without a separate C# 15 setting
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
2026-06-17 23:34:53 +02:00
Siegfried PammerandClaude Opus 4.7 1a297bebb2 Handle non-Task task-likes (ValueTask, custom AsyncMethodBuilder) in ILReader async return-type unpacking.
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) <[email protected]>
2026-05-20 22:30:41 +02:00
Siegfried Pammer 82edef3bc6 Add scaffolding for the C# 15 runtime-async lowering.
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.
2026-05-20 22:30:41 +02:00
Siegfried PammerandClaude Opus 4.7 317e33b3b8 Fix five small analyzer warnings.
* 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) <[email protected]>
2026-05-08 21:22:48 +02:00
Siegfried Pammer ade13f848e Apply suggestions from Claude code review 2026-03-20 19:39:07 +01:00
Siegfried Pammer 91d7a76b94 Fix typo in MetadataModule.GetDefinition 2026-03-19 21:56:21 +01:00
Siegfried Pammer 98d764a169 Add a treenode and decompiler APIs for extensions. 2026-03-16 22:41:21 +01:00
Siegfried Pammer 77d9ee73e9 Fix IDE2000 globally 2025-12-29 19:24:31 +01:00
Siegfried Pammer 5f0e767c7d Fix crash in TryEncodingV2, if there are multiple extension groups with the same target type, but different extension parameter name. 2025-12-25 07:58:52 +01:00
ds5678 83df0ab9b9 Handle multiple calling conventions at once 2025-11-30 12:10:04 -08:00
ds5678 a04c0e4cab Improve decompilation of unmanaged function pointers
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.
2025-11-30 12:10:04 -08:00
Siegfried Pammer 396b58031b Add support for C# 14 first-class span types in the type system. 2025-11-21 21:39:09 +01:00
Siegfried Pammer 1fe8771bc6 Fixed issue described in PR #3596: Derived record classes do not necessarily declare a ToString override, as the core logic is in PrintMembers. 2025-11-02 14:11:44 +01:00
Siegfried Pammer f6c763e7bb Add tests for ExtensionEncodingV1 and ExtensionEncodingV2. 2025-11-02 08:49:59 +01:00
ds5678 1d964bce8e Fix regression in decompiling local functions with default parameters 2025-09-14 14:11:52 +02:00
Siegfried Pammer 7f84d24167 Add support for new extension everything encoding. 2025-09-13 07:20:50 +02:00
Siegfried Pammer 17a533647b Add C# 14 ExtensionMarkerAttribute 2025-09-09 21:41:25 +02:00
Siegfried Pammer 732f285c41 Rename KnownAttributes.RequiredAttribute to KnownAttributes.Required 2025-09-09 21:39:44 +02:00
Peter Crabtree e1e16b64f5 dev: Strip BOM mark from text files 2025-08-28 20:56:19 -04:00
Siegfried Pammer 3b0939d99c Add ExtensionInfo: mapping of extension members to extension implementations and more. 2025-08-04 08:53:33 +02:00
Siegfried Pammer 68ae8ae833 Rename ITypeDefinition.HasExtensionMethods to ITypeDefinition.HasExtensions 2025-08-04 08:53:33 +02:00
Siegfried Pammer 7ea2c8f32f Remove dead code 2025-08-02 15:40:54 +02:00
Siegfried Pammer c642678f7f Remove ToTypeReference: Implement ParseReflectionName with SRM TypeName. 2025-08-02 15:32:11 +02:00
Siegfried Pammer 33e497eb80 Refactor ParseReflectionName to return IType instead of ITypeReference 2025-08-02 11:39:01 +02:00
Siegfried Pammer c9e3790adc Remove UnresolvedUsingScope 2025-08-02 11:00:31 +02:00
Daniel Grunwald ccb8887657 Move AllowByRefLike to SRMExtensions. 2025-08-01 09:37:58 +02:00
Jan Jones 2357d00939 Support allows ref struct constraint 2025-07-27 18:25:08 +02:00
Maximilian Schmöcker 53d01869c9 Fix incorrect method for static methods with instance signature 2025-07-05 16:48:58 +02:00
Siegfried Pammer 4aa777ccc2 Add IsArrayInterfaceType extension method 2025-07-04 19:08:07 +02:00
ds5678 586c04d081 Support params keyword on non-array collections 2025-07-04 19:08:07 +02:00
Siegfried Pammer 101ddf8783 Add support for constant slices of InlineArrays 2025-06-15 17:22:07 +02:00
Siegfried Pammer 667036c54e Add support for InlineArrayConversion 2025-06-15 17:22:07 +02:00
Jeremy PrittsandSiegfried Pammer c1ecf1ae9c Handle explicit optional parameter after default parameter (#3470)
* Handle explicit optional parameter after default parameter

* Rename and add documentation

* Move extension method

* Add unit test for lamba function

* Apply suggestions from code review

Co-authored-by: Siegfried Pammer <[email protected]>

---------

Co-authored-by: Siegfried Pammer <[email protected]>
2025-05-17 17:11:15 +02:00
Siegfried Pammer e5a85422d3 Fix #3457: NRE when creating Mermaid diagram of assembly with (possibly) missing references. 2025-04-13 21:41:54 +02:00