194 Commits
Author SHA1 Message Date
Siegfried Pammer 3e45005ebd Emit params and default values on lambda parameter lists
C# 12 allows both on the explicitly typed parameter list of a lambda, and
nowhere else: an anonymous method cannot declare either, and neither can a
lambda whose parameter list is about to be dropped. Guarded by a setting so
the output stays valid for earlier language versions.

Only what the anonymous function's own metadata declares is written. A lambda
may state a default the delegate does not have, a different one, or none where
the delegate has one, and reflection over the lambda's method reports what the
lambda declared - so filling either in from the delegate's Invoke would make
the recompiled assembly describe itself differently from the original. Call
sites are unaffected either way, because they bind against the delegate, which
still declares both.

Roslyn writes ParamArrayAttribute on the anonymous function's own method only
from version 5 on; before that it stands on the delegate type alone, where it
is not the lambda's to restate, so the fixture guards those cases on ROSLYN5.
The correctness test reads the metadata back through reflection, which is the
only place the difference is observable.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-30 13:49:33 +02:00
Christoph Wille fe9f6db3ee Emit C# 7.1 default literals where the context supplies the type
Shortening default(T) is the same problem as removing the redundant cast
around a lambda whose delegate type the context already fixes, so it uses
the same mechanism: ConvertTo makes the explicit type implicit when the
conversion is an identity conversion and the caller allows an implicit
one. The literal keeps the type it was shortened from, so any later
conversion to a different type - or any context that requires an explicit
type, such as an overload resolution recheck falling back to CastArguments
- can spell default(T) out again. That keeps the value intact where the
bare literal would change it, e.g. "object o = default(SomeStruct)", which
boxes a non-null struct while "default" would be null.

Because the shortened literal resolves to DefaultLiteralResolveResult,
CallBuilder's existing overload resolution recheck sees a real default
literal and rejects ambiguous calls on its own; no separate bookkeeping
about which arguments may stay untyped is needed. Only the contexts that
supply no target type at all restore the explicit form: an awaited
expression, and arguments of operator methods, which later become operator
or cast syntax rather than calls.

Assisted-by: Claude:claude-opus-5[1m]:Claude Code
2026-08-22 21:35:22 +02:00
Siegfried Pammer 3da129632e Fix #3624: decompile semi-auto properties with the C# 14 field keyword
Backing-field references inside a property's own get/set/init accessors
are emitted as the `field` keyword at IL-to-AST translation time
(ExpressionBuilder.ConvertField), so arbitrary accessor bodies become
expressible and no separate rewrite pass is needed. Compiler-generated
trivial accessors then collapse individually to `get;`/`set;`, which
also handles mixed shapes like `{ get; set { ... field ... } }`; the
backing-field declaration is removed with its remaining attributes
re-hosted as `field:` sections, and constructor stores become property
initializers (or property assignments, for setter-less properties).
Implicit zero-stores that auto-default struct constructors emit for
unassigned backing fields are dropped rather than lifted.

Recognition stays AST/metadata-based rather than mirroring the ILAst
analysis used for automatic events: events must prove compiler-generated
bodies before discarding them, while the field keyword discards nothing,
so name association plus the accessor context is sufficient.

Below C# 14 (or with the new FieldKeyword setting off), the field
declaration survives under its metadata name, so the UI keeps showing
the truth; EscapeInvalidIdentifiers - the transform the compilable-output
flows (project export, VS, tests) already add - now maps
`<P>k__BackingField` to the readable `P__BackingField` instead of the
generic character escape. A genuine field literally named "field" is
qualified as `this.field` inside accessors, and locals are not named
"field" there, since C# 14 rebinds the bare identifier. Bodiless
accessors mixed into multi-line properties get their own line in the
output.

The fixture covering the feature surface lands with the implementation rather
than as a separate xfailed commit. It is excluded from the test-assembly
compilation because its nullable annotations would trip warnings-as-errors
there.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-13 13:15:21 +02:00
Siegfried Pammer 08b12055d0 Derive the C# 1.0 setting categories from the version map
The 14 C# 1.0 settings each carried a handwritten
[Category("C# 1.0 / VS .NET")] literal, duplicating the per-version
display knowledge the generator's CategoryByVersion map single-sources.
Gating them on LanguageVersion.CSharp1 instead is observably identical:
CSharp1 is the smallest enum value, so the generated SetLanguageVersion
bucket can never fire, and the new GetMinimumRequiredVersion arm returns
the same CSharp1 the final fallback already does.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer 590793fd21 Document the two roles of the language version in the settings API
The language version appears in two places that share a name but not a
concept, which repeatedly reads as one confused API: on
DecompilerSettings it is a construction shortcut (SetLanguageVersion
initializes the feature flags once and the version is not stored, so
the flags are the only state and the call is deliberately one-way),
while on WholeProjectDecompiler it is an export parameter (the
LangVersion stamped into the project file, defaulting to
GetMinimumRequiredVersion() and rejected below it as a safety net
against exporting uncompilable projects). Spell both roles out in the
XML docs so the distinction no longer has to be reverse-engineered.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer f39cd0d006 Use the DecompilerSettings.Other resource key consistently
Four settings used the bare category string "Other" while the rest of
the group uses the "DecompilerSettings.Other" resource key. Both happen
to resolve to the same English text today, so the options UI shows one
group, but the two keys would split into separate groups the moment
their translations diverge.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer 0d0e88a61b Gate SwitchOnReadOnlySpanChar on C# 11.0
The setting carried the C# 11.0 display category but was missing from
both SetLanguageVersion and GetMinimumRequiredVersion, so decompiling
for an older target language version could still produce switches over
ReadOnlySpan<char> that the requested compiler cannot compile. Gating
it like the other C# 11.0 settings closes that gap; the category string
is now derived from the version like everywhere else.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer 401530b86d Let ExtensionMethods, UseLambdaSyntax and UseEnhancedUsing raise the minimum version
These three settings were disabled by SetLanguageVersion for older
targets but, unlike every comparable syntax-preference setting, never
raised GetMinimumRequiredVersion while enabled - an omission that had
gone unnoticed in the handwritten version bookkeeping. Drop the
AffectsMinimumRequiredVersion escape hatch that reproduced it.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer b75ea27800 Generate DecompilerSettings boilerplate from [DecompilerSetting] attributes
Every version-gated setting was bookkept in four places that had to stay
in sync by hand: the property boilerplate, SetLanguageVersion,
GetMinimumRequiredVersion, and the [Category] display string - and that
sync had already drifted in a handful of settings. A new source
generator in ICSharpCode.Decompiler.Generators now derives all four
from a single [DecompilerSetting] attribute on a partial property:
backing field, accessors with change notification, the version-derived
[Category], and both version methods. [Description] stays handwritten
because its resource keys are irregular and are grepped from the resx.

This commit is a 1:1 translation: the current inconsistencies are
reproduced exactly (AffectsMinimumRequiredVersion = false on
ExtensionMethods, UseLambdaSyntax and UseEnhancedUsing; no gate on
SwitchOnReadOnlySpanChar), verified against the old build by comparing
SetLanguageVersion and GetMinimumRequiredVersion behavior for every
setting at every language version, plus a reflection diff of the full
per-property attribute surface.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-08-09 17:11:41 +02:00
Siegfried Pammer 847324e836 Fix #3845: Add option to expand XML documentation comments
Doc-comment folds were created with defaultCollapsed hardcoded to true,
so /// blocks always started collapsed. The new Display option is
bridged into DecompilerSettings like the other expand flags and drives
the fold's default state.

Detecting the last line of a doc-comment block requires sibling
navigation between trivia nodes, so trivia now carry parent, sibling
list, and index state. That state lives on the Trivia subclass to keep
AstNode's size unchanged, and the tree API treats trivia as a separate
navigation space: Next/PrevSibling and Remove work within the owning
list, Slot is null, ReplaceWith throws instead of aliasing the trivia
index into the parent's child slots, GetNextNode/GetPrevNode stop at
the list boundary, Clone and CopyAnnotationsFrom deep-copy and reparent
trivia, and CheckInvariant validates the trivia lists.

Assisted-by: Claude:claude-fable-5:Claude Code
2026-07-04 10:23:56 +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
danbopesandClaude Opus 4.7 01b968fbbd Add opt-in SortSwitchSections setting (#3749). (#3750)
When enabled, switch sections are ordered by their case label value
instead of by the underlying branch's IL offset. Default is false to
keep existing output unchanged. Useful when diffing decompiler output
across rebuilds of obfuscated assemblies, where IL block layout is
unstable but the case-to-value mapping is not.

Includes an ILPretty test that exercises a hand-written switch whose
table targets are placed at non-monotonic IL offsets (simulating
obfuscator block shuffling) and verifies the cases come out in
label-value order with the setting enabled. Also adds the
Resources.resx / Resources.Designer.cs entry so the WPF settings UI
shows a proper label instead of the raw key.

Co-authored-by: Claude Opus 4.7 (1M context) <[email protected]>
2026-06-10 20:08:51 +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 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 94050a4aca Rewrite TransformFieldAndConstructorInitializers from a step-by-step AST-based analysis to an analysis that tracks the whole constructor body. 2025-11-20 10:19:47 +01:00
Siegfried Pammer 9c8d1e48d9 Many thanks to @sonyps5201314 for providing the test cases and a suggested fix, which inspired these changes.
Various improvements regarding primary constructor decompilation, including:
- introduce `HasPrimaryConstructor` property in the AST, as there is a difference between no primary constructor and a parameterless primary constructor
- improved support for inherited records and forwarded ctor calls
- exclude non-public fields and properties in IsPrintedMember
- introduce an option to always make the decompiler emit primary constructors, when possible
2025-11-12 00:29:51 +01:00
Peter Crabtree e1e16b64f5 dev: Strip BOM mark from text files 2025-08-28 20:56:19 -04:00
Jan Kučera 9d11f523b1 Always move initializer decompilation setting 2025-08-17 04:39:17 +01:00
Maximilian Schmöcker 43bfaba7e0 Add ExpandParamsArguments DecompilerSettings 2025-08-06 21:53:07 +02:00
Siegfried Pammer 29bd78a9c9 Add DecompilerSettings.ExtensionMembers and Language version 14 2025-08-04 08:53:33 +02:00
Siegfried Pammer 49368caf1c Rename option to "UseObjectCreationOfGenericTypeParameter" 2025-07-12 14:24:08 +02:00
DoctorKrolic 47eeacd7aa Add an option to not transform Activator.CreateInstance<T>() to new T() 2025-07-12 14:24:02 +02:00
ds5678 586c04d081 Support params keyword on non-array collections 2025-07-04 19:08:07 +02:00
Siegfried Pammer 79f7a188b0 Add support for C# 12 inline array expressions 2025-06-15 17:22:07 +02:00
Siegfried Pammer 82e461be8c Change return type of Clone method in derived class. 2025-06-02 08:13:12 +02:00
ds5678 361bb17618 Add configuration option to check for overflow and underflow 2025-05-29 14:47:24 -07:00
Siegfried Pammer 6c72d1c5f0 Add transform to remove unconstrained generic reference type check. 2025-04-07 23:10:06 +02:00
Siegfried Pammer 96caa4ecb7 Fix: #3407 Add "private protected" feature for 7.2 decompiler options 2025-03-22 12:27:22 +01:00
Siegfried Pammer 8b76879493 Fix #2716: Add an option to allow sorting custom attributes 2025-02-20 22:23:57 +01:00
Siegfried Pammer 2043e5dd6f Add support for C# 12 primary constructors. 2024-08-11 11:32:15 +02:00
Siegfried Pammer 02d2a8c1f8 Add metadata processing for C# 12 'ref readonly' parameters 2024-07-19 18:40:08 +02:00
Daniel Grunwald d48df11133 Remove unused transforms: DecimalConstantTransform and ParameterNullCheckTransform 2024-07-19 09:23:17 +02:00
Siegfried Pammer 1370b999fe Fix #2893: Add option to disable automatic assembly loading.
This setting is honored by all functionality that triggers a decompilation run. It is ignored by features that load assemblies as their primary function. For example, using the "Load Dependencies" feature will still resolve and load assemblies from the file-system. The same happens when you double-click on an assembly reference in the tree view. It will be resolved and loaded.

Note that disabling automatic assembly load will cause the decompiler to potentially not be able to resolve types from references that have not been added manually and the quality of the decompiled code will be inferior as a result.
2024-01-07 18:05:46 +01:00
Siegfried Pammer 70616b301c Implement support for C# 11 switch on (ReadOnly)Span<char>. 2023-08-17 00:16:41 +02:00
Siegfried Pammer 688474facd Add missing DecompilerSettings for new language features 2023-08-05 14:33:47 +02:00
Christoph Wille 397661b2ec Merge pull request #3041 from icsharpcode/roslyn46
Roslyn 4.6
2023-07-26 07:23:58 +02:00
Daniel Grunwald 99d5e94a62 Allow inlining value type temporaries into constrained call. 2023-07-22 22:05:39 +02:00
Daniel Grunwald 32fafeb9a1 Add support for user-defined checked operators. 2023-06-03 17:45:14 +02:00
Daniel Grunwald 3c46271a11 Add support for unsigned right shift.
Only for user-defined operators so far; builtin right shifts still cast to the appropriate type.
2023-06-02 21:37:42 +02:00
Daniel Grunwald 3dc2f3d5b6 Rename lifetime annotation to ScopedRef 2023-05-30 11:32:25 +02:00
Daniel Grunwald f568123704 Add support for UTF8 string literals 2023-05-30 11:20:20 +02:00
Daniel Grunwald 9359d47c0b Add support for ScopedRefAttribute 2023-05-29 18:59:49 +02:00
Daniel Grunwald efeaf1356f Add feature: C#11 nint without NativeIntegerAttribute
Because it is no longer possible to distinguish IntPtr from nint, this required a lot of testcase adjustment.
2023-05-29 14:07:06 +02:00
Daniel Grunwald f3013010cd Add support for C# 11 required members. 2022-11-10 22:13:12 +01:00
hexafluoride fc6ae4c645 Add option to always fully qualify type names with global:: 2022-08-30 16:15:42 +02:00
Daniel Grunwald a06de7171c Fix comments 2022-07-23 20:57:12 +02:00
Siegfried Pammer 2ed9ad6b51 Add support for C# 11 scoped parameter modifier. 2022-07-16 16:20:18 +02:00
Siegfried Pammer d248867302 Add support for C# 10 record structs. 2022-05-26 15:18:22 +02:00
Siegfried Pammer 68b388bc36 Disable DecompilerSettings.ParameterNullCheck 2022-04-19 12:40:29 +02:00
Siegfried Pammer 9e462b53ad Add support for C# 11 parameter null checks 2022-04-16 20:42:48 +02:00