Skip to content

Commit 3c70224

Browse files
Fix detection and display of explicitly implemented operators.
1 parent 31bbcf4 commit 3c70224

File tree

4 files changed

+86
-10
lines changed

4 files changed

+86
-10
lines changed

ICSharpCode.Decompiler.Tests/Output/CSharpAmbienceTests.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -283,7 +283,17 @@ public void AutomaticProperty(ConversionFlags flags, string expectedOutput)
283283
[TestCase(ILSpyMainTreeViewMemberFlags, "this[int] : int")]
284284
public void Indexer(ConversionFlags flags, string expectedOutput)
285285
{
286-
var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer).Single();
286+
var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && !p.IsExplicitInterfaceImplementation).Single();
287+
ambience.ConversionFlags = flags;
288+
289+
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput));
290+
}
291+
292+
[TestCase(StandardConversionFlags, "int Interface.this[int index] { get; }")]
293+
[TestCase(ILSpyMainTreeViewMemberFlags, "Interface.this[int] : int")]
294+
public void ExplicitIndexer(ConversionFlags flags, string expectedOutput)
295+
{
296+
var prop = compilation.FindType(typeof(CSharpAmbienceTests.Program)).GetProperties(p => p.IsIndexer && p.IsExplicitInterfaceImplementation).Single();
287297
ambience.ConversionFlags = flags;
288298

289299
Assert.That(ambience.ConvertSymbol(prop), Is.EqualTo(expectedOutput));
@@ -323,7 +333,12 @@ ref struct RefStruct { }
323333
readonly struct ReadonlyStruct { }
324334
readonly ref struct ReadonlyRefStruct { }
325335

326-
class Program
336+
interface Interface
337+
{
338+
int this[int x] { get; }
339+
}
340+
341+
class Program : Interface
327342
{
328343
int test;
329344
const int TEST2 = 2;
@@ -336,6 +351,12 @@ public int this[int index] {
336351
}
337352
}
338353

354+
int Interface.this[int index] {
355+
get {
356+
return index;
357+
}
358+
}
359+
339360
public event EventHandler ProgramChanged;
340361

341362
public event EventHandler SomeEvent {

ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -279,9 +279,20 @@ void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormat
279279
ConvertType(member.DeclaringType, writer, formattingPolicy);
280280
writer.WriteToken(Roles.Dot, ".");
281281
}
282+
IType explicitInterfaceType = GetExplicitInterfaceType(member);
283+
string name = member.Name;
284+
if (explicitInterfaceType != null)
285+
{
286+
name = name.Substring(name.LastIndexOf('.') + 1);
287+
}
282288
switch (member.SymbolKind)
283289
{
284290
case SymbolKind.Indexer:
291+
if (explicitInterfaceType != null)
292+
{
293+
ConvertType(explicitInterfaceType, writer, formattingPolicy);
294+
writer.WriteToken(Roles.Dot, ".");
295+
}
285296
writer.WriteKeyword(Roles.Identifier, "this");
286297
break;
287298
case SymbolKind.Constructor:
@@ -292,11 +303,16 @@ void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormat
292303
WriteQualifiedName(member.DeclaringType.Name, writer, formattingPolicy);
293304
break;
294305
case SymbolKind.Operator:
295-
switch (member.Name)
306+
switch (name)
296307
{
297308
case "op_Implicit":
298309
writer.WriteKeyword(OperatorDeclaration.ImplicitRole, "implicit");
299310
writer.Space();
311+
if (explicitInterfaceType != null)
312+
{
313+
ConvertType(explicitInterfaceType, writer, formattingPolicy);
314+
writer.WriteToken(Roles.Dot, ".");
315+
}
300316
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
301317
writer.Space();
302318
ConvertType(member.ReturnType, writer, formattingPolicy);
@@ -305,19 +321,29 @@ void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormat
305321
case "op_CheckedExplicit":
306322
writer.WriteKeyword(OperatorDeclaration.ExplicitRole, "explicit");
307323
writer.Space();
324+
if (explicitInterfaceType != null)
325+
{
326+
ConvertType(explicitInterfaceType, writer, formattingPolicy);
327+
writer.WriteToken(Roles.Dot, ".");
328+
}
308329
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
309330
writer.Space();
310-
if (member.Name == "op_CheckedExplicit")
331+
if (name == "op_CheckedExplicit")
311332
{
312333
writer.WriteToken(OperatorDeclaration.CheckedKeywordRole, "checked");
313334
writer.Space();
314335
}
315336
ConvertType(member.ReturnType, writer, formattingPolicy);
316337
break;
317338
default:
339+
if (explicitInterfaceType != null)
340+
{
341+
ConvertType(explicitInterfaceType, writer, formattingPolicy);
342+
writer.WriteToken(Roles.Dot, ".");
343+
}
318344
writer.WriteKeyword(OperatorDeclaration.OperatorKeywordRole, "operator");
319345
writer.Space();
320-
var operatorType = OperatorDeclaration.GetOperatorType(member.Name);
346+
var operatorType = OperatorDeclaration.GetOperatorType(name);
321347
if (operatorType.HasValue && !((ConversionFlags & ConversionFlags.SupportOperatorChecked) == 0 && OperatorDeclaration.IsChecked(operatorType.Value)))
322348
{
323349
if (OperatorDeclaration.IsChecked(operatorType.Value))
@@ -335,7 +361,12 @@ void WriteMemberDeclarationName(IMember member, TokenWriter writer, CSharpFormat
335361
}
336362
break;
337363
default:
338-
writer.WriteIdentifier(Identifier.Create(member.Name));
364+
if (explicitInterfaceType != null)
365+
{
366+
ConvertType(explicitInterfaceType, writer, formattingPolicy);
367+
writer.WriteToken(Roles.Dot, ".");
368+
}
369+
writer.WriteIdentifier(Identifier.Create(name));
339370
break;
340371
}
341372
WriteTypeParameters(node, writer, formattingPolicy);
@@ -407,6 +438,17 @@ public void ConvertType(IType type, TokenWriter writer, CSharpFormattingOptions
407438
astType.AcceptVisitor(new CSharpOutputVisitor(writer, formattingPolicy));
408439
}
409440

441+
IType GetExplicitInterfaceType(IMember member)
442+
{
443+
if (member.IsExplicitInterfaceImplementation)
444+
{
445+
var baseMember = member.ExplicitlyImplementedInterfaceMembers.FirstOrDefault();
446+
if (baseMember != null)
447+
return baseMember.DeclaringType;
448+
}
449+
return null;
450+
}
451+
410452
public string ConvertConstantValue(object constantValue)
411453
{
412454
return TextWriterTokenWriter.PrintPrimitiveValue(constantValue);

ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1784,10 +1784,6 @@ public EntityDeclaration ConvertEntity(IEntity entity)
17841784
case SymbolKind.Event:
17851785
return ConvertEvent((IEvent)entity);
17861786
case SymbolKind.Method:
1787-
if (entity.Name.Contains(".op_"))
1788-
{
1789-
goto case SymbolKind.Operator;
1790-
}
17911787
return ConvertMethod((IMethod)entity);
17921788
case SymbolKind.Operator:
17931789
return ConvertOperator((IMethod)entity);

ICSharpCode.Decompiler/TypeSystem/Implementation/MetadataMethod.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,23 @@ internal MetadataMethod(MetadataModule module, MethodDefinitionHandle handle)
9696
this.symbolKind = SymbolKind.Destructor;
9797
}
9898
}
99+
else if ((attributes & MethodAttributes.Static) != 0 && typeParameters.Length == 0)
100+
{
101+
// Operators that are explicit interface implementations are not marked
102+
// with MethodAttributes.SpecialName or MethodAttributes.RTSpecialName
103+
string name = this.Name;
104+
int index = name.LastIndexOf('.');
105+
if (index > 0)
106+
{
107+
name = name.Substring(index + 1);
108+
109+
if (name.StartsWith("op_", StringComparison.Ordinal)
110+
&& CSharp.Syntax.OperatorDeclaration.GetOperatorType(name) != null)
111+
{
112+
this.symbolKind = SymbolKind.Operator;
113+
}
114+
}
115+
}
99116
this.IsExtensionMethod = (attributes & MethodAttributes.Static) == MethodAttributes.Static
100117
&& (module.TypeSystemOptions & TypeSystemOptions.ExtensionMethods) == TypeSystemOptions.ExtensionMethods
101118
&& def.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.Extension);

0 commit comments

Comments
 (0)