Skip to content

Dedicated UI and Decompiler APIs for C# 14 extensions#3680

Open
siegfriedpammer wants to merge 7 commits intomasterfrom
csharp14/extensions
Open

Dedicated UI and Decompiler APIs for C# 14 extensions#3680
siegfriedpammer wants to merge 7 commits intomasterfrom
csharp14/extensions

Conversation

@siegfriedpammer
Copy link
Member

No description provided.

@christophwille
Copy link
Member

@dgrunwald if you got a minute to spare, maybe you can have a look too. Thanks.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 ExtensionTreeNode and extension overlay icon support, and updates member/type icons to carry an isExtension overlay flag.
  • Updates/renames pretty test coverage to a broader ExtensionEverything scenario.

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));
Comment on lines +61 to +64
return ((MetadataModule)ParentAssemblyNode.LoadedAssembly
.GetMetadataFileOrNull()
?.GetTypeSystemWithCurrentOptionsOrNull(SettingsService, AssemblyTreeModel.CurrentLanguageVersion)
?.MainModule)?.GetDefinition((SRM.TypeDefinitionHandle)MarkerMethod.DeclaringTypeDefinition.MetadataToken);
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))
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;
@christophwille christophwille added UI Decompiler The decompiler engine itself labels Mar 18, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Decompiler The decompiler engine itself UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants