Repair private virtual member output

Metadata can contain private virtual members that C# cannot declare directly. Adjusting the emitted accessibility in the decompiler keeps such members compilable without pushing the policy into the general AST builder.

Assisted-by: OpenCode:openai/gpt-5.5:OpenCode
This commit is contained in:
Siegfried Pammer
2026-10-03 14:52:35 +02:00
parent e528a358ed
commit ddcac25c2b
4 changed files with 70 additions and 0 deletions
@@ -117,6 +117,12 @@ namespace ICSharpCode.Decompiler.Tests
await Run();
}
[Test]
public async Task Issue3136()
{
await Run();
}
[Test]
public async Task Issue1918()
{
@@ -0,0 +1,16 @@
using System.Runtime.CompilerServices;
public class MyClass
{
private object _MyValue;
protected virtual object MyValue {
get {
return _MyValue;
}
[MethodImpl(MethodImplOptions.Synchronized)]
set {
_MyValue = value;
}
}
}
@@ -0,0 +1,35 @@
.class public auto ansi beforefieldinit MyClass extends [mscorlib]System.Object
{
.field private object _MyValue
.method public hidebysig specialname rtspecialname instance void .ctor () cil managed
{
.maxstack 8
ldarg.0
call instance void [mscorlib]System.Object::.ctor()
ret
} // end of method MyClass::.ctor
.method private specialname newslot strict virtual instance object get_MyValue () cil managed
{
.maxstack 8
ldarg.0
ldfld object MyClass::_MyValue
ret
} // end of method MyClass::get_MyValue
.method private specialname newslot strict virtual instance void set_MyValue (object 'value') cil managed synchronized
{
.maxstack 8
ldarg.0
ldarg.1
stfld object MyClass::_MyValue
ret
} // end of method MyClass::set_MyValue
.property instance object MyValue()
{
.get instance object MyClass::get_MyValue()
.set instance void MyClass::set_MyValue(object)
} // end of property MyClass::MyValue
}
@@ -2245,6 +2245,7 @@ namespace ICSharpCode.Decompiler.CSharp
// without this the decompiled program does not compile (CS5001).
methodDecl.Name = "Main";
}
FixPrivateVirtualMemberModifiers(methodDecl, method);
FixParameterNames(methodDecl);
var methodDefinition = metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
if (!settings.LocalFunctions && LocalFunctionDecompiler.LocalFunctionNeedsAccessibilityChange(method.ParentModule!.MetadataFile, (MethodDefinitionHandle)method.MetadataToken))
@@ -2824,6 +2825,7 @@ namespace ICSharpCode.Decompiler.CSharp
int lastDot = property.Name.LastIndexOf('.');
propertyDecl.Name = property.Name.Substring(lastDot + 1);
}
FixPrivateVirtualMemberModifiers(propertyDecl, property);
FixParameterNames(propertyDecl);
Accessor? getter, setter;
if (propertyDecl is PropertyDeclaration)
@@ -2905,6 +2907,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
eventDecl.Name = ev.Name.Substring(lastDot + 1);
}
FixPrivateVirtualMemberModifiers(eventDecl, ev);
if (isAutomaticEvent)
{
AutoEventDecompiler.AddFieldLikeEventAttributes((EventDeclaration)eventDecl, typeSystemAstBuilder, ev, backingField!);
@@ -2941,6 +2944,16 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
static void FixPrivateVirtualMemberModifiers(EntityDeclaration declaration, IMember member)
{
if (member.Accessibility != Accessibility.Private || member.DeclaringType.Kind == TypeKind.Interface)
return;
if (!member.IsVirtual && !member.IsAbstract && !member.IsOverride)
return;
declaration.Modifiers &= ~Modifiers.Private;
declaration.Modifiers |= Modifiers.Protected;
}
#region Sequence Points
/// <summary>
/// Creates sequence points for the given syntax tree.