diff --git a/ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs b/ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs index 63eec4cd3..446dbde02 100644 --- a/ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs +++ b/ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs @@ -283,7 +283,17 @@ namespace ICSharpCode.Decompiler.Tests.Output [TestCase(ILSpyMainTreeViewMemberFlags, "this[int] : int")] public void Indexer(ConversionFlags flags, string expectedOutput) { - var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer).Single(); + var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation).Single(); + ambience.ConversionFlags = flags; + + Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); + } + + [TestCase(StandardConversionFlags, "int Interface.this[int index] { get; }")] + [TestCase(ILSpyMainTreeViewMemberFlags, "Interface.this[int] : int")] + public void ExplicitIndexer(ConversionFlags flags, string expectedOutput) + { + var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && p.IsExplicitInterfaceImplementation).Single(); ambience.ConversionFlags = flags; Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput)); @@ -323,7 +333,12 @@ namespace ICSharpCode.Decompiler.Tests.Output readonly struct ReadonlyStruct { } readonly ref struct ReadonlyRefStruct { } - class Program + interface Interface + { + int this[int x] { get; } + } + + class Program : Interface { int test; const int TEST2 = 2; @@ -336,6 +351,12 @@ namespace ICSharpCode.Decompiler.Tests.Output } } + int Interface.this[int index] { + get { + return index; + } + } + public event EventHandler ProgramChanged; public event EventHandler SomeEvent { diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs index beac7989c..aa290cbd8 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs @@ -279,9 +279,20 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor ConvertType(member.DeclaringType, writer, formattingPolicy); writer.WriteToken(Roles.Dot, "."); } + IType explicitInterfaceType = GetExplicitInterfaceType(member); + string name = member.Name; + if (explicitInterfaceType != null) + { + name = name.Substring(name.LastIndexOf('.') + 1); + } switch (member.SymbolKind) { case SymbolKind.Indexer: + if (explicitInterfaceType != null) + { + ConvertType(explicitInterfaceType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } writer.WriteKeyword(Roles.Identifier, "this"); break; case SymbolKind.Constructor: @@ -292,11 +303,16 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy); break; case SymbolKind.Operator: - switch (member.Name) + switch (name) { case "op_Implicit": writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit"); writer.Space(); + if (explicitInterfaceType != null) + { + ConvertType(explicitInterfaceType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); ConvertType(member.ReturnType, writer, formattingPolicy); @@ -305,9 +321,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor case "op_CheckedExplicit": writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit"); writer.Space(); + if (explicitInterfaceType != null) + { + ConvertType(explicitInterfaceType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); - if (member.Name == "op_CheckedExplicit") + if (name == "op_CheckedExplicit") { writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked"); writer.Space(); @@ -315,9 +336,14 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor ConvertType(member.ReturnType, writer, formattingPolicy); break; default: + if (explicitInterfaceType != null) + { + ConvertType(explicitInterfaceType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator"); writer.Space(); - var operatorType = OperatorDeclaration.GetOperatorType(member.Name); + var operatorType = OperatorDeclaration.GetOperatorType(name); if (operatorType.HasValue && !((ConversionFlags & ConversionFlags.SupportOperatorChecked) == 0 && OperatorDeclaration.IsChecked(operatorType.Value))) { if (OperatorDeclaration.IsChecked(operatorType.Value)) @@ -335,7 +361,12 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor } break; default: - writer.WriteIdentifier(Identifier.Create(member.Name)); + if (explicitInterfaceType != null) + { + ConvertType(explicitInterfaceType, writer, formattingPolicy); + writer.WriteToken(Roles.Dot, "."); + } + writer.WriteIdentifier(Identifier.Create(name)); break; } WriteTypeParameters(node, writer, formattingPolicy); @@ -407,6 +438,17 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy)); } + IType GetExplicitInterfaceType(IMember member) + { + if (member.IsExplicitInterfaceImplementation) + { + var baseMember = member.ExplicitlyImplementedInterfaceMembers.FirstOrDefault(); + if (baseMember != null) + return baseMember.DeclaringType; + } + return null; + } + public string ConvertConstantValue(object constantValue) { return TextWriterTokenWriter.PrintPrimitiveValue(constantValue); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs index c9ae2a66d..63b697d75 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs @@ -1784,10 +1784,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax case SymbolKind.Event: return ConvertEvent((IEvent)entity); case SymbolKind.Method: - if (entity.Name.Contains(".op_")) - { - goto case SymbolKind.Operator; - } return ConvertMethod((IMethod)entity); case SymbolKind.Operator: return ConvertOperator((IMethod)entity); diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs index 83946c9fa..7173d1850 100644 --- a/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs +++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs @@ -96,6 +96,23 @@ namespace ICSharpCode.Decompiler.TypeSystem.Implementation this.symbolKind = SymbolKind.Destructor; } } + else if ((attributes & MethodAttributes.Static) != 0 && typeParameters.Length == 0) + { + // Operators that are explicit interface implementations are not marked + // with MethodAttributes.SpecialName or MethodAttributes.RTSpecialName + string name = this.Name; + int index = name.LastIndexOf('.'); + if (index > 0) + { + name = name.Substring(index + 1); + + if (name.StartsWith("op_", StringComparison.Ordinal) + && CSharp.Syntax.OperatorDeclaration.GetOperatorType(name) != null) + { + this.symbolKind = SymbolKind.Operator; + } + } + } this.IsExtensionMethod = (attributes & MethodAttributes.Static) == MethodAttributes.Static && (module.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods && def.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension);