Browse Source

Fix #3610: Add more primary constructor test cases.

pull/3613/head
Siegfried Pammer 2 months ago
parent
commit
cb48768b6d
  1. 74
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Playstation.cs
  2. 74
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/PlaystationPreferPrimary.cs
  3. 12
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs
  4. 51
      ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

74
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Playstation.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
@ -226,6 +227,79 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Playstation @@ -226,6 +227,79 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Playstation
}
}
internal class Issue3610
{
private struct CtorDoubleAssignmentTest
{
#if EXPECTED_OUTPUT
public bool Value = false;
public CtorDoubleAssignmentTest(string arg1, int arg2)
{
Value = true;
}
#else
public bool Value;
public CtorDoubleAssignmentTest(string arg1, int arg2)
{
Value = false;
Value = true;
}
#endif
}
private struct CtorDoubleAssignmentTest2
{
public bool Value = true;
public CtorDoubleAssignmentTest2(string arg1, int arg2)
{
Value = false;
}
}
private class FieldInitTest
{
public bool Flag = true;
public Func<int, int> Action = (int a) => a;
public string Value;
public FieldInitTest(string value)
{
Value = value;
}
}
private abstract class PCFieldInitTest(StringComparison value)
{
private StringComparison _value = value;
public bool Func()
{
return value == StringComparison.Ordinal;
}
}
private class RecordTest<T>
{
private interface IInterface
{
T[] Objects { get; }
}
protected record Record(T[] Objects) : IInterface
{
public Record(List<T> objects)
: this(objects.ToArray())
{
}
}
}
private abstract record RecordTest2(Guid[] Guids);
}
public record NamedParameter(string name, object? value, bool encode = true) : Parameter(Ensure.NotEmptyString(name, "name"), value, encode);
[DebuggerDisplay("{DebuggerDisplay()}")]

74
ICSharpCode.Decompiler.Tests/TestCases/Pretty/PlaystationPreferPrimary.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
@ -201,6 +202,79 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.PlaystationPreferPrimary @@ -201,6 +202,79 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.PlaystationPreferPrimary
}
}
internal class Issue3610
{
private struct CtorDoubleAssignmentTest
{
#if EXPECTED_OUTPUT
public bool Value = false;
public CtorDoubleAssignmentTest(string arg1, int arg2)
{
Value = true;
}
#else
public bool Value;
public CtorDoubleAssignmentTest(string arg1, int arg2)
{
Value = false;
Value = true;
}
#endif
}
private struct CtorDoubleAssignmentTest2
{
public bool Value = true;
public CtorDoubleAssignmentTest2(string arg1, int arg2)
{
Value = false;
}
}
private class FieldInitTest
{
public bool Flag = true;
public Func<int, int> Action = (int a) => a;
public string Value;
public FieldInitTest(string value)
{
Value = value;
}
}
private abstract class PCFieldInitTest(StringComparison value)
{
private StringComparison _value = value;
public bool Func()
{
return value == StringComparison.Ordinal;
}
}
private class RecordTest<T>
{
private interface IInterface
{
T[] Objects { get; }
}
protected record Record(T[] Objects) : IInterface
{
public Record(List<T> objects)
: this(objects.ToArray())
{
}
}
}
private abstract record RecordTest2(Guid[] Guids);
}
public record NamedParameter(string name, object? value, bool encode = true) : Parameter(Ensure.NotEmptyString(name, "name"), value, encode);
[DebuggerDisplay("{DebuggerDisplay()}")]

12
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -177,6 +177,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -177,6 +177,8 @@ namespace ICSharpCode.Decompiler.CSharp
Dictionary<IMethod, IMethod> ctorCallChain = new Dictionary<IMethod, IMethod>();
Accessibility minAccessibility = recordTypeDef.IsAbstract ? Accessibility.Protected : Accessibility.Public;
foreach (var method in recordTypeDef.Methods)
{
cancellationToken.ThrowIfCancellationRequested();
@ -185,10 +187,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -185,10 +187,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.IsStatic || !method.IsConstructor)
continue;
var m = method.Specialize(subst);
var body = DecompileBody(method, allTransforms: method.Accessibility == Accessibility.Public && !recordTypeDef.IsRecord);
var body = DecompileBody(method, allTransforms: method.Accessibility >= minAccessibility && !recordTypeDef.IsRecord);
if (body == null)
continue;
if (primaryCtor == null && method.Accessibility == Accessibility.Public && IsPrimaryConstructorBody(m, method, body))
if (primaryCtor == null && method.Accessibility >= minAccessibility && IsPrimaryConstructorBody(m, method, body))
{
primaryCtor = method;
}
@ -199,7 +201,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -199,7 +201,7 @@ namespace ICSharpCode.Decompiler.CSharp
// if a chained to a constructor of a different type, give up
if (calledCtor != null && calledCtor.DeclaringTypeDefinition?.Equals(method.DeclaringTypeDefinition) != true)
return null;
ctorCallChain[method] = calledCtor;
ctorCallChain[method] = (IMethod)calledCtor?.MemberDefinition;
}
if (primaryCtor == null)
{
@ -272,6 +274,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -272,6 +274,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
if (offset < method.Parameters.Count)
{
if (primaryCtorParameterToAutoPropertyOrBackingField.ContainsKey(unspecializedMethod.Parameters[offset]))
return false;
if (autoPropertyOrBackingFieldToPrimaryCtorParameter.ContainsKey(backingMember))
return false;
primaryCtorParameterToAutoPropertyOrBackingField.Add(unspecializedMethod.Parameters[offset], backingMember);
autoPropertyOrBackingFieldToPrimaryCtorParameter.Add(backingMember, unspecializedMethod.Parameters[offset]);
}

51
ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

@ -264,7 +264,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -264,7 +264,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
bool referencesParameter = initializer.Annotations.OfType<ILInstruction>().Any(inst => inst.Descendants
.OfType<IInstructionWithVariableOperand>()
.Any(inst => inst.Variable.Kind == VariableKind.Parameter));
.Any(inst => inst.Variable.Kind == VariableKind.Parameter && ctorMethodDef.Equals(inst.Variable.Function.Method)));
if (referencesParameter)
break;
}
@ -287,44 +287,33 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -287,44 +287,33 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
if (allSame)
{
foreach (var ctor in instanceCtorsNotChainingWithThis)
ctor.Body.First().Remove();
if (fieldOrPropertyOrEventDecl == null)
continue;
if (ctorIsUnsafe && IntroduceUnsafeModifier.IsUnsafe(initializer))
if (fieldOrPropertyOrEventDecl is PropertyDeclaration { Initializer.IsNull: true } pd)
{
fieldOrPropertyOrEventDecl.Modifiers |= Modifiers.Unsafe;
pd.Initializer = initializer.Detach();
}
if (fieldOrPropertyOrEventDecl is PropertyDeclaration pd)
else if (fieldOrPropertyOrEventDecl is FieldDeclaration or EventDeclaration)
{
pd.Initializer = initializer.Detach();
var vdecl = fieldOrPropertyOrEventDecl.GetChildrenByRole(Roles.Variable).Single();
if (vdecl.Initializer.IsNull)
{
vdecl.Initializer = initializer.Detach();
}
else
{
break;
}
}
else
if (ctorIsUnsafe && fieldOrPropertyOrEventDecl != null && IntroduceUnsafeModifier.IsUnsafe(initializer))
{
fieldOrPropertyOrEventDecl.GetChildrenByRole(Roles.Variable).Single().Initializer = initializer.Detach();
fieldOrPropertyOrEventDecl.Modifiers |= Modifiers.Unsafe;
}
foreach (var ctor in instanceCtorsNotChainingWithThis)
ctor.Body.First().Remove();
}
} while (allSame);
}
}
bool IsPropertyDeclaredByPrimaryCtor(IMember m, RecordDecompiler record)
{
if (record == null)
return false;
switch (m)
{
case IProperty p:
return record.IsPropertyDeclaredByPrimaryConstructor(p);
case IField f:
return record.PrimaryConstructor != null;
case IEvent e:
return record.PrimaryConstructor != null;
default:
return false;
}
}
void RemoveSingleEmptyConstructor(IEnumerable<AstNode> members, ITypeDefinition contextTypeDefinition)
{
// if we're outside of a type definition skip this altogether
@ -390,9 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -390,9 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
continue;
}
var fieldOrPropertyDecl = members.FirstOrDefault(f => f.GetSymbol() == fieldOrProperty) as EntityDeclaration;
if (fieldOrPropertyDecl == null)
break;
if (ctorIsUnsafe && IntroduceUnsafeModifier.IsUnsafe(assignment.Right))
if (ctorIsUnsafe && fieldOrPropertyDecl != null && IntroduceUnsafeModifier.IsUnsafe(assignment.Right))
{
fieldOrPropertyDecl.Modifiers |= Modifiers.Unsafe;
}
@ -416,7 +403,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -416,7 +403,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
}
}
else if (fieldOrPropertyDecl is PropertyDeclaration { IsAutomaticProperty: true } pd)
else if (fieldOrPropertyDecl is PropertyDeclaration { IsAutomaticProperty: true, Initializer.IsNull: true } pd)
{
pd.Initializer = assignment.Right.Detach();
}

Loading…
Cancel
Save