Dedicated UI and Decompiler APIs for C# 14 extensions#3680
Open
siegfriedpammer wants to merge 7 commits intomasterfrom
Open
Dedicated UI and Decompiler APIs for C# 14 extensions#3680siegfriedpammer wants to merge 7 commits intomasterfrom
siegfriedpammer wants to merge 7 commits intomasterfrom
Conversation
Member
|
@dgrunwald if you got a minute to spare, maybe you can have a look too. Thanks. |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds first-class support for C# “extension” declarations across the decompiler and ILSpy UI, including extension-group discovery, decompilation entry points, and updated icon overlays.
Changes:
- Introduces
ExtensionInfo.ExtensionGroups+ group/member APIs and wires them into decompilation (DecompileExtension) and namespace collection. - Adds an
ExtensionTreeNodeand extension overlay icon support, and updates member/type icons to carry anisExtensionoverlay flag. - Updates/renames pretty test coverage to a broader
ExtensionEverythingscenario.
Reviewed changes
Copilot reviewed 32 out of 33 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| ILSpy/TreeNodes/TypeTreeNode.cs | Adds extension-group nodes to the type tree; updates icon overlay call signature. |
| ILSpy/TreeNodes/PropertyTreeNode.cs | Updates metadata resolution + specialization; adds extension-aware icons and extension decompilation routing. |
| ILSpy/TreeNodes/MethodTreeNode.cs | Updates metadata resolution + specialization; adds extension-aware icons, extension decompilation routing, and hides implementation methods. |
| ILSpy/TreeNodes/ILSpyTreeNode.cs | Enables nullable context (file-local) and adds a helper for obtaining the current type system. |
| ILSpy/TreeNodes/FieldTreeNode.cs | Updates icon overlay call signature. |
| ILSpy/TreeNodes/ExtensionTreeNode.cs | New node for extension groups in the tree view, including filtering and extension decompilation. |
| ILSpy/TreeNodes/EventTreeNode.cs | Updates icon overlay call signature. |
| ILSpy/TreeNodes/AssemblyTreeNode.cs | Uses current-options type system creation for executable module loading. |
| ILSpy/Metadata/CorTables/EventTableTreeNode.cs | Switches to shared type system helper. |
| ILSpy/Languages/CSharpLanguage.cs | Adds DecompileExtension overloads for type/method/property handles. |
| ILSpy/Images/OverlayExtension.xaml | Adds WPF drawing resource for extension overlay. |
| ILSpy/Images/OverlayExtension.svg | Adds SVG source for the extension overlay. |
| ILSpy/Images/MemberIcon.cs | Removes dedicated ExtensionMethod icon in favor of overlay approach. |
| ILSpy/Images/Images.cs | Adds extension overlay handling + extends icon cache key to include isExtension. |
| ILSpy/ExtensionMethods.cs | Makes language version parameter nullable for current-options type system creation. |
| ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs | Adds ResolveExtensionInfo(IMember) helper. |
| ICSharpCode.Decompiler/TypeSystem/ITypeDefinition.cs | Documents semantics of ExtensionInfo and repositions the member. |
| ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataTypeDefinition.cs | Gates ExtensionInfo behind TypeSystemOptions.ExtensionMembers. |
| ICSharpCode.Decompiler/TypeSystem/IMethod.cs | Clarifies IsExtensionMethod semantics for “classic” extension methods. |
| ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs | Refactors grouping API and adds ExtensionGroups + member enumeration per group. |
| ICSharpCode.Decompiler/TypeSystem/DecompilerTypeSystem.cs | Introduces TypeSystemOptions.ExtensionMembers and hooks it to settings. |
| ICSharpCode.Decompiler/Output/TextTokenWriter.cs | Refactors brace folding decision into NeedsFold. |
| ICSharpCode.Decompiler/Output/IAmbience.cs | Adds SupportExtensionDeclarations conversion flag. |
| ICSharpCode.Decompiler/IL/Transforms/TransformDisplayClassUsage.cs | Fixes field identifier escaping regression (@field). |
| ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj | Bumps language version. |
| ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs | Adds SupportExtensionDeclarations + ConvertExtension builder. |
| ICSharpCode.Decompiler/CSharp/RequiredNamespaceCollector.cs | Avoids collecting marker/group nested types; remaps to container type. |
| ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs | Renders extension marker types as extension(...) when flag enabled. |
| ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs | Adds DecompileExtension(EntityHandle) and integrates extension member handling. |
| ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExtensionProperties.cs | Removes old pretty test case. |
| ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExtensionEverything.cs | Adds expanded pretty test case for extensions. |
| ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs | Renames test entry to match new pretty test case. |
| ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj | Updates compile include to new pretty test case file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+257
to
+276
| if (isExtension) | ||
| { | ||
| group.Children.Add(baseDrawing); | ||
| var extensionGroup = new DrawingGroup(); | ||
| extensionGroup.Children.Add(baseDrawingGroup); | ||
| baseDrawingGroup.Transform = new ScaleTransform(0.8, 0.8); | ||
| extensionGroup.Children.Add(new ImageDrawing(Images.OverlayExtension, iconRect)); | ||
| baseDrawingGroup = extensionGroup; | ||
| } | ||
|
|
||
| group.Children.Add(baseDrawingGroup); | ||
|
|
||
| if (isStatic) | ||
| { | ||
| group.Children.Add(new ImageDrawing(Images.OverlayStatic, iconRect)); | ||
| } | ||
|
|
||
| if (overlay != null) | ||
| { | ||
| baseDrawingGroup.Transform = new ScaleTransform(0.8, 0.8); | ||
| group.Children.Add(new ImageDrawing(overlay, iconRect)); |
ILSpy/TreeNodes/ExtensionTreeNode.cs
Outdated
Comment on lines
+61
to
+64
| return ((MetadataModule)ParentAssemblyNode.LoadedAssembly | ||
| .GetMetadataFileOrNull() | ||
| ?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion) | ||
| ?.MainModule)?.GetDefinition((SRM.TypeDefinitionHandle)MarkerMethod.DeclaringTypeDefinition.MetadataToken); |
ILSpy/TreeNodes/ExtensionTreeNode.cs
Outdated
Comment on lines
+67
to
+76
| protected override void LoadChildren() | ||
| { | ||
| var extensionInfo = ContainerTypeDefinition.ExtensionInfo; | ||
| var subst = new TypeParameterSubstitution(TypeParameters, null); | ||
|
|
||
| foreach (var property in extensionInfo.GetMembersOfGroup(MarkerMethod).OfType<IProperty>().OrderBy(p => p.Name, NaturalStringComparer.Instance)) | ||
| { | ||
| this.Children.Add(new PropertyTreeNode(property)); | ||
| } | ||
| foreach (var method in extensionInfo.GetMembersOfGroup(MarkerMethod).OfType<IMethod>().OrderBy(m => m.Name, NaturalStringComparer.Instance)) |
ILSpy/TreeNodes/ExtensionTreeNode.cs
Outdated
| protected override void LoadChildren() | ||
| { | ||
| var extensionInfo = ContainerTypeDefinition.ExtensionInfo; | ||
| var subst = new TypeParameterSubstitution(TypeParameters, null); |
Comment on lines
+102
to
+103
| Debug.Assert(language is CSharpLanguage); | ||
| ((CSharpLanguage)language).DecompileExtension(GetTypeDefinition(), output, options); |
Comment on lines
69
to
+74
| public static ImageSource GetIcon(IProperty property) | ||
| { | ||
| IMethod accessor = property.Getter ?? property.Setter; | ||
| bool isExtension = property.ResolveExtensionInfo()?.InfoOfExtensionMember((IMethod)accessor.MemberDefinition) != null; | ||
| return Images.GetIcon(property.IsIndexer ? MemberIcon.Indexer : MemberIcon.Property, | ||
| Images.GetOverlayIcon(property.Accessibility), property.IsStatic); | ||
| Images.GetOverlayIcon(property.Accessibility), property.IsStatic, isExtension); |
Comment on lines
+97
to
+103
| var extensionInfo = TypeDefinition.ExtensionInfo; | ||
| if (extensionInfo != null) | ||
| { | ||
| foreach (var extensionGroup in TypeDefinition.ExtensionInfo?.ExtensionGroups ?? []) | ||
| { | ||
| this.Children.Add(new ExtensionTreeNode(TypeDefinition, extensionGroup, ParentAssemblyNode)); | ||
| } |
| public SyntaxTree DecompileExtension(EntityHandle handle) | ||
| { | ||
| if (handle.IsNil) | ||
| throw new ArgumentNullException(nameof(handle)); |
| IProperty propDef = module.GetDefinition((PropertyDefinitionHandle)handle); | ||
| extensionInfo = propDef.ResolveExtensionInfo(); | ||
| Debug.Assert(extensionInfo != null); | ||
| var accessor = propDef.Getter ?? propDef.Setter; |
d135dae to
ade13f8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.