diff --git a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs index 3c32788a9..514da7f0f 100644 --- a/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs +++ b/ICSharpCode.Decompiler.Tests/TypeSystem/TypeSystemTestCase.cs @@ -785,5 +785,51 @@ namespace ICSharpCode.Decompiler.Tests.TypeSystem { } } + + extension(int number) + { + public int Squared => number * number; + } + + extension(string input) + { + public void Method() { } + public void Method(char c) { } + public string AsString => input.ToString(); + public string Test { + get => "Test"; + set { } + } + public static void StaticMethodOnString() { } + public static void StaticMethodOnString(double x) { } + public static string StaticPropertyOnString => "StaticProperty"; + public static void GenericMethodOnString(T value) + { + } + } + + extension(T input) + { + public void Method() { } + public void Method(char c) { } + public string AsString => input.ToString(); + public string Test { + get => "Test"; + set { } + } + public static void StaticMethodOnGeneric() { } + public static void StaticMethodOnGeneric(double x) { } + public static string StaticPropertyOnGeneric => "StaticProperty"; + public static void GenericMethodOnGeneric(U value) + { + } + } + + extension(T input) + { + public void StaticMethodOnGenericTwoParams(T2 x) + { + } + } } -} +} \ No newline at end of file diff --git a/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs b/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs index c2e28cac6..c9b45c668 100644 --- a/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs +++ b/ICSharpCode.Decompiler/TypeSystem/ExtensionInfo.cs @@ -40,14 +40,14 @@ namespace ICSharpCode.Decompiler.TypeSystem var metadata = module.MetadataFile.Metadata; - foreach (var extGroup in extensionContainer.NestedTypes) + foreach (var nestedType in extensionContainer.NestedTypes) { - if (TryEncodingV1(extGroup)) + if (TryEncodingV1(nestedType)) { continue; } - TryEncodingV2(extGroup); + TryEncodingV2(nestedType); } bool TryEncodingV1(ITypeDefinition extGroup) @@ -90,43 +90,52 @@ namespace ICSharpCode.Decompiler.TypeSystem return true; } - bool TryEncodingV2(ITypeDefinition extGroup) + bool TryEncodingV2(ITypeDefinition extensionGroupsContainer) { - if (!(extGroup is { Kind: TypeKind.Class, IsSealed: true } - && extGroup.Name.StartsWith("$", StringComparison.Ordinal))) + // there exists one nested type per extension target type + if (!(extensionGroupsContainer is { Kind: TypeKind.Class, IsSealed: true } + && extensionGroupsContainer.Name.StartsWith("$", StringComparison.Ordinal))) { return false; } - var markerType = extGroup.NestedTypes.SingleOrDefault(t => t.Name.StartsWith("$", StringComparison.Ordinal) && t.IsStatic); - var marker = markerType?.Methods.SingleOrDefault(m => m.Name == "$" && m.IsStatic && m.Parameters.Count == 1); + // if there are multiple extension-blocks with the same target type, + // but different names for the extension parameter, + // there is a separate markerType, so there are multiple marker types per + // target type + foreach (var markerType in extensionGroupsContainer.NestedTypes) + { + if (!(markerType.Name.StartsWith("$", StringComparison.Ordinal) && markerType.IsStatic)) + continue; + var marker = markerType.Methods.SingleOrDefault(m => m.Name == "$" && m.IsStatic && m.Parameters.Count == 1); + if (marker == null) + continue; - if (markerType == null || marker == null) - return false; + TypeDefinition td = metadata.GetTypeDefinition((TypeDefinitionHandle)extensionGroupsContainer.MetadataToken); + List extensionMethods = []; + ITypeParameter[] extensionGroupTypeParameters = new ITypeParameter[extensionGroupsContainer.TypeParameterCount]; - TypeDefinition td = metadata.GetTypeDefinition((TypeDefinitionHandle)extGroup.MetadataToken); - List extensionMethods = []; - ITypeParameter[] extensionGroupTypeParameters = new ITypeParameter[extGroup.TypeParameterCount]; + // For easier access to accessors we use SRM + foreach (var h in td.GetMethods()) + { + var method = module.GetDefinition(h); - // For easier access to accessors we use SRM - foreach (var h in td.GetMethods()) - { - var method = module.GetDefinition(h); + if (method.SymbolKind is SymbolKind.Constructor) + continue; - if (method.SymbolKind is SymbolKind.Constructor) - continue; + var attribute = method.GetAttribute(KnownAttribute.ExtensionMarker); + if (attribute == null) + continue; - var attribute = method.GetAttribute(KnownAttribute.ExtensionMarker); - if (attribute == null) - continue; + if (attribute.FixedArguments[0].Value?.ToString() != markerType.Name) + continue; - if (attribute.FixedArguments[0].Value?.ToString() != markerType.Name) - continue; + extensionMethods.Add(method); + } - extensionMethods.Add(method); + CollectImplementationMethods(extensionGroupsContainer, marker, extensionMethods, extensionGroupTypeParameters); } - CollectImplementationMethods(extGroup, marker, extensionMethods, extensionGroupTypeParameters); return true; }