Browse Source

Merge pull request #2476 from yyjdelete/records

Fix #2475: Add support for sealed records and records with interface
pull/2523/head
Siegfried Pammer 4 years ago committed by GitHub
parent
commit
a08ef0ecb3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 37
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
  2. 26
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  3. 222
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs
  4. 2
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  5. 11
      ICSharpCode.Decompiler/CSharp/Transforms/TransformFieldAndConstructorInitializers.cs

37
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs

@ -1,7 +1,16 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{ {
public record Base(string A); public record Base(string A);
public record CopyCtor(string A)
{
protected CopyCtor(CopyCtor _)
{
}
}
public record Derived(int B) : Base(B.ToString()); public record Derived(int B) : Base(B.ToString());
public record Empty; public record Empty;
@ -15,6 +24,12 @@
public string S = "abc"; public string S = "abc";
} }
public record Interface(int B) : IRecord;
public interface IRecord
{
}
public record Pair<A, B> public record Pair<A, B>
{ {
public A First { get; init; } public A First { get; init; }
@ -24,13 +39,17 @@
public record PairWithPrimaryCtor<A, B>(A First, B Second); public record PairWithPrimaryCtor<A, B>(A First, B Second);
public record PrimaryCtor(int A, string B); public record PrimaryCtor(int A, string B);
public record PrimaryCtorWithAttribute([RecordTest("param")] [property: RecordTest("property")][field: RecordTest("field")] int a);
public record PrimaryCtorWithField(int A, string B) public record PrimaryCtorWithField(int A, string B)
{ {
public double C; public double C = 1.0;
public string D = A + B;
} }
public record PrimaryCtorWithInParameter(in int A, in string B);
public record PrimaryCtorWithProperty(int A, string B) public record PrimaryCtorWithProperty(int A, string B)
{ {
public double C { get; init; } public double C { get; init; } = 1.0;
public string D { get; } = A + B;
} }
public record Properties public record Properties
@ -48,6 +67,18 @@
} }
} }
[AttributeUsage(AttributeTargets.All)]
public class RecordTestAttribute : Attribute
{
public RecordTestAttribute(string name)
{
}
}
public sealed record Sealed(string A);
public sealed record SealedDerived(int B) : Base(B.ToString());
public class WithExpressionTests public class WithExpressionTests
{ {
public Fields Test(Fields input) public Fields Test(Fields input)

26
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1227,7 +1227,31 @@ namespace ICSharpCode.Decompiler.CSharp
if (recordDecompiler?.PrimaryConstructor != null) if (recordDecompiler?.PrimaryConstructor != null)
{ {
foreach (var p in recordDecompiler.PrimaryConstructor.Parameters) foreach (var p in recordDecompiler.PrimaryConstructor.Parameters)
typeDecl.PrimaryConstructorParameters.Add(typeSystemAstBuilder.ConvertParameter(p)); {
ParameterDeclaration pd = typeSystemAstBuilder.ConvertParameter(p);
(IProperty prop, IField field) = recordDecompiler.GetPropertyInfoByPrimaryConstructorParameter(p);
Syntax.Attribute[] attributes = prop.GetAttributes().Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray();
if (attributes.Length > 0)
{
var section = new AttributeSection {
AttributeTarget = "property"
};
section.Attributes.AddRange(attributes);
pd.Attributes.Add(section);
}
attributes = field.GetAttributes()
.Where(a => !PatternStatementTransform.attributeTypesToRemoveFromAutoProperties.Contains(a.AttributeType.FullName))
.Select(attr => typeSystemAstBuilder.ConvertAttribute(attr)).ToArray();
if (attributes.Length > 0)
{
var section = new AttributeSection {
AttributeTarget = "field"
};
section.Attributes.AddRange(attributes);
pd.Attributes.Add(section);
}
typeDecl.PrimaryConstructorParameters.Add(pd);
}
} }
foreach (var type in typeDef.NestedTypes) foreach (var type in typeDef.NestedTypes)

222
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -37,6 +37,8 @@ namespace ICSharpCode.Decompiler.CSharp
readonly CancellationToken cancellationToken; readonly CancellationToken cancellationToken;
readonly List<IMember> orderedMembers; readonly List<IMember> orderedMembers;
readonly bool isInheritedRecord; readonly bool isInheritedRecord;
readonly bool isStruct;
readonly bool isSealed;
readonly IMethod primaryCtor; readonly IMethod primaryCtor;
readonly IType baseClass; readonly IType baseClass;
readonly Dictionary<IField, IProperty> backingFieldToAutoProperty = new Dictionary<IField, IProperty>(); readonly Dictionary<IField, IProperty> backingFieldToAutoProperty = new Dictionary<IField, IProperty>();
@ -51,7 +53,9 @@ namespace ICSharpCode.Decompiler.CSharp
this.settings = settings; this.settings = settings;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.baseClass = recordTypeDef.DirectBaseTypes.FirstOrDefault(b => b.Kind == TypeKind.Class); this.baseClass = recordTypeDef.DirectBaseTypes.FirstOrDefault(b => b.Kind == TypeKind.Class);
this.isInheritedRecord = !baseClass.IsKnownType(KnownTypeCode.Object); this.isStruct = baseClass.IsKnownType(KnownTypeCode.ValueType);
this.isInheritedRecord = !isStruct && !baseClass.IsKnownType(KnownTypeCode.Object);
this.isSealed = recordTypeDef.IsSealed;
DetectAutomaticProperties(); DetectAutomaticProperties();
this.orderedMembers = DetectMemberOrder(recordTypeDef, backingFieldToAutoProperty); this.orderedMembers = DetectMemberOrder(recordTypeDef, backingFieldToAutoProperty);
this.primaryCtor = DetectPrimaryConstructor(); this.primaryCtor = DetectPrimaryConstructor();
@ -164,13 +168,15 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.IsStatic || !method.IsConstructor) if (method.IsStatic || !method.IsConstructor)
continue; continue;
var m = method.Specialize(subst); var m = method.Specialize(subst);
if (IsPrimaryConstructor(m)) if (IsPrimaryConstructor(m, method))
return method; return method;
primaryCtorParameterToAutoProperty.Clear();
autoPropertyToPrimaryCtorParameter.Clear();
} }
return null; return null;
bool IsPrimaryConstructor(IMethod method) bool IsPrimaryConstructor(IMethod method, IMethod unspecializedMethod)
{ {
Debug.Assert(method.IsConstructor); Debug.Assert(method.IsConstructor);
var body = DecompileBody(method); var body = DecompileBody(method);
@ -180,28 +186,37 @@ namespace ICSharpCode.Decompiler.CSharp
if (method.Parameters.Count == 0) if (method.Parameters.Count == 0)
return false; return false;
if (body.Instructions.Count != method.Parameters.Count + 2) var addonInst = isStruct ? 1 : 2;
if (body.Instructions.Count < method.Parameters.Count + addonInst)
return false; return false;
for (int i = 0; i < body.Instructions.Count - 2; i++) for (int i = 0; i < method.Parameters.Count; i++)
{ {
if (!body.Instructions[i].MatchStFld(out var target, out var field, out var valueInst)) if (!body.Instructions[i].MatchStFld(out var target, out var field, out var valueInst))
return false; return false;
if (!target.MatchLdThis()) if (!target.MatchLdThis())
return false; return false;
if (method.Parameters[i].IsIn)
{
if (!valueInst.MatchLdObj(out valueInst, out _))
return false;
}
if (!valueInst.MatchLdLoc(out var value)) if (!valueInst.MatchLdLoc(out var value))
return false; return false;
if (!(value.Kind == VariableKind.Parameter && value.Index == i)) if (!(value.Kind == VariableKind.Parameter && value.Index == i))
return false; return false;
if (!backingFieldToAutoProperty.TryGetValue(field, out var property)) if (!backingFieldToAutoProperty.TryGetValue(field, out var property))
return false; return false;
primaryCtorParameterToAutoProperty.Add(method.Parameters[i], property); primaryCtorParameterToAutoProperty.Add(unspecializedMethod.Parameters[i], property);
autoPropertyToPrimaryCtorParameter.Add(property, method.Parameters[i]); autoPropertyToPrimaryCtorParameter.Add(property, unspecializedMethod.Parameters[i]);
} }
var baseCtorCall = body.Instructions.SecondToLastOrDefault() as CallInstruction; if (!isStruct)
if (baseCtorCall == null) {
return false; var baseCtorCall = body.Instructions.SecondToLastOrDefault() as CallInstruction;
if (baseCtorCall == null)
return false;
}
var returnInst = body.Instructions.LastOrDefault(); var returnInst = body.Instructions.LastOrDefault();
return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop(); return returnInst != null && returnInst.MatchReturn(out var retVal) && retVal.MatchNop();
@ -233,6 +248,8 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary> /// </summary>
public IMethod PrimaryConstructor => primaryCtor; public IMethod PrimaryConstructor => primaryCtor;
public bool IsInheritedRecord => isInheritedRecord;
bool IsRecordType(IType type) bool IsRecordType(IType type)
{ {
return type.GetDefinition() == recordTypeDef return type.GetDefinition() == recordTypeDef
@ -244,13 +261,9 @@ namespace ICSharpCode.Decompiler.CSharp
/// </summary> /// </summary>
public bool MethodIsGenerated(IMethod method) public bool MethodIsGenerated(IMethod method)
{ {
if (method.IsConstructor) if (IsCopyConstructor(method))
{ {
if (method.Parameters.Count == 1 return IsGeneratedCopyConstructor(method);
&& IsRecordType(method.Parameters[0].Type))
{
return IsGeneratedCopyConstructor(method);
}
} }
switch (method.Name) switch (method.Name)
@ -309,7 +322,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
switch (property.Name) switch (property.Name)
{ {
case "EqualityContract": case "EqualityContract" when !isStruct:
return IsGeneratedEqualityContract(property); return IsGeneratedEqualityContract(property);
default: default:
return IsPropertyDeclaredByPrimaryConstructor(property); return IsPropertyDeclaredByPrimaryConstructor(property);
@ -323,6 +336,24 @@ namespace ICSharpCode.Decompiler.CSharp
&& autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst)); && autoPropertyToPrimaryCtorParameter.ContainsKey((IProperty)property.Specialize(subst));
} }
internal (IProperty prop, IField field) GetPropertyInfoByPrimaryConstructorParameter(IParameter parameter)
{
var prop = primaryCtorParameterToAutoProperty[parameter];
return (prop, autoPropertyToBackingField[prop]);
}
public bool IsCopyConstructor(IMethod method)
{
if (method == null)
return false;
Debug.Assert(method.DeclaringTypeDefinition == recordTypeDef);
return method.IsConstructor
&& method.Parameters.Count == 1
&& IsRecordType(method.Parameters[0].Type);
}
private bool IsGeneratedCopyConstructor(IMethod method) private bool IsGeneratedCopyConstructor(IMethod method)
{ {
/* /*
@ -333,7 +364,7 @@ namespace ICSharpCode.Decompiler.CSharp
Debug.Assert(method.IsConstructor && method.Parameters.Count == 1); Debug.Assert(method.IsConstructor && method.Parameters.Count == 1);
if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any()) if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any())
return false; return false;
if (method.Accessibility != Accessibility.Protected) if (method.Accessibility != Accessibility.Protected && (!isSealed || method.Accessibility != Accessibility.Private))
return false; return false;
if (orderedMembers == null) if (orderedMembers == null)
return false; return false;
@ -393,10 +424,10 @@ namespace ICSharpCode.Decompiler.CSharp
// protected virtual Type EqualityContract { // protected virtual Type EqualityContract {
// [CompilerGenerated] get => typeof(R); // [CompilerGenerated] get => typeof(R);
// } // }
Debug.Assert(property.Name == "EqualityContract"); Debug.Assert(!isStruct && property.Name == "EqualityContract");
if (property.Accessibility != Accessibility.Protected) if (property.Accessibility != Accessibility.Protected && (!isSealed || property.Accessibility != Accessibility.Private))
return false; return false;
if (!(property.IsVirtual || property.IsOverride)) if (!(isSealed || property.IsVirtual || property.IsOverride))
return false; return false;
if (property.IsSealed) if (property.IsSealed)
return false; return false;
@ -428,11 +459,11 @@ namespace ICSharpCode.Decompiler.CSharp
Debug.Assert(method.Name == "PrintMembers"); Debug.Assert(method.Name == "PrintMembers");
if (method.Parameters.Count != 1) if (method.Parameters.Count != 1)
return false; return false;
if (!method.IsOverridable) if (!isSealed && !method.IsOverridable)
return false; return false;
if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any()) if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any())
return false; return false;
if (method.Accessibility != Accessibility.Protected) if (method.Accessibility != Accessibility.Protected && (!isSealed || method.Accessibility != Accessibility.Private))
return false; return false;
if (orderedMembers == null) if (orderedMembers == null)
return false; return false;
@ -444,6 +475,15 @@ namespace ICSharpCode.Decompiler.CSharp
if (builder.Type.ReflectionName != "System.Text.StringBuilder") if (builder.Type.ReflectionName != "System.Text.StringBuilder")
return false; return false;
int pos = 0; int pos = 0;
//Roslyn 4.0.0-3.final start to insert an call to RuntimeHelpers.EnsureSufficientExecutionStack()
if (!isStruct && !isInheritedRecord && body.Instructions[pos] is Call
{
Arguments: { Count: 0 },
Method: { Name: "EnsureSufficientExecutionStack", DeclaringType: { Namespace: "System.Runtime.CompilerServices", Name: "RuntimeHelpers" } }
})
{
pos++;
}
if (isInheritedRecord) if (isInheritedRecord)
{ {
// Special case: inherited record adding no new members // Special case: inherited record adding no new members
@ -551,7 +591,7 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
return false; // static fields/properties are not printed return false; // static fields/properties are not printed
} }
if (member.Name == "EqualityContract") if (!isStruct && member.Name == "EqualityContract")
{ {
return false; // EqualityContract is never printed return false; // EqualityContract is never printed
} }
@ -617,7 +657,8 @@ namespace ICSharpCode.Decompiler.CSharp
// if (callvirt PrintMembers(ldloc this, ldloc stringBuilder)) { trueInst } // if (callvirt PrintMembers(ldloc this, ldloc stringBuilder)) { trueInst }
if (!body.Instructions[3].MatchIfInstruction(out var condition, out var trueInst)) if (!body.Instructions[3].MatchIfInstruction(out var condition, out var trueInst))
return true; return true;
if (!(condition is CallVirt { Method: { Name: "PrintMembers" } } printMembersCall)) if (!((condition is CallInstruction { Method: { Name: "PrintMembers" } } printMembersCall) &&
(condition is CallVirt || (isSealed && condition is Call))))
return false; return false;
if (printMembersCall.Arguments.Count != 2) if (printMembersCall.Arguments.Count != 2)
return false; return false;
@ -640,21 +681,20 @@ namespace ICSharpCode.Decompiler.CSharp
return false; return false;
return toStringCall.Arguments[0].MatchLdLoc(stringBuilder); return toStringCall.Arguments[0].MatchLdLoc(stringBuilder);
bool MatchAppendCall(ILInstruction inst, out string val) bool MatchAppendCallWithValue(ILInstruction inst, string val)
{ {
val = null;
if (!(inst is CallVirt { Method: { Name: "Append" } } call)) if (!(inst is CallVirt { Method: { Name: "Append" } } call))
return false; return false;
if (call.Arguments.Count != 2) if (call.Arguments.Count != 2)
return false; return false;
if (!call.Arguments[0].MatchLdLoc(stringBuilder)) if (!call.Arguments[0].MatchLdLoc(stringBuilder))
return false; return false;
return call.Arguments[1].MatchLdStr(out val); //Roslyn 4.0.0-3.final start to use char for 1 length string
} if (call.Method.Parameters[0].Type.IsKnownType(KnownTypeCode.Char))
{
bool MatchAppendCallWithValue(ILInstruction inst, string val) return val != null && val.Length == 1 && call.Arguments[1].MatchLdcI4(val[0]);
{ }
return MatchAppendCall(inst, out string tmp) && tmp == val; return call.Arguments[1].MatchLdStr(out string val1) && val1 == val;
} }
} }
@ -670,7 +710,7 @@ namespace ICSharpCode.Decompiler.CSharp
Debug.Assert(method.Name == "Equals" && method.Parameters.Count == 1); Debug.Assert(method.Name == "Equals" && method.Parameters.Count == 1);
if (method.Parameters.Count != 1) if (method.Parameters.Count != 1)
return false; return false;
if (!method.IsOverridable) if (!isSealed && !method.IsOverridable)
return false; return false;
if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any()) if (method.GetAttributes().Any() || method.GetReturnTypeAttributes().Any())
return false; return false;
@ -698,58 +738,61 @@ namespace ICSharpCode.Decompiler.CSharp
var conditions = UnpackLogicAndChain(returnValue); var conditions = UnpackLogicAndChain(returnValue);
Debug.Assert(conditions.Count >= 1); Debug.Assert(conditions.Count >= 1);
int pos = 0; int pos = 0;
if (isInheritedRecord) if (!isStruct)
{
// call BaseClass::Equals(ldloc this, ldloc other)
if (pos >= conditions.Count)
return false;
if (!(conditions[pos] is Call { Method: { Name: "Equals" } } call))
return false;
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType, baseClass))
return false;
if (call.Arguments.Count != 2)
return false;
if (!call.Arguments[0].MatchLdThis())
return false;
if (!call.Arguments[1].MatchLdLoc(other))
return false;
pos++;
}
else
{ {
// comp.o(ldloc other != ldnull) if (isInheritedRecord)
if (pos >= conditions.Count) {
return false; // call BaseClass::Equals(ldloc this, ldloc other)
if (!conditions[pos].MatchCompNotEqualsNull(out var arg)) if (pos >= conditions.Count)
return false; return false;
if (!arg.MatchLdLoc(other)) if (!(conditions[pos] is Call { Method: { Name: "Equals" } } call))
return false; return false;
pos++; if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(call.Method.DeclaringType, baseClass))
// call op_Equality(callvirt get_EqualityContract(ldloc this), callvirt get_EqualityContract(ldloc other)) return false;
// Special-cased because Roslyn isn't using EqualityComparer<T> here. if (call.Arguments.Count != 2)
if (pos >= conditions.Count) return false;
return false; if (!call.Arguments[0].MatchLdThis())
if (!(conditions[pos] is Call { Method: { IsOperator: true, Name: "op_Equality" } } opEqualityCall)) return false;
return false; if (!call.Arguments[1].MatchLdLoc(other))
if (!opEqualityCall.Method.DeclaringType.IsKnownType(KnownTypeCode.Type)) return false;
return false; pos++;
if (opEqualityCall.Arguments.Count != 2) }
return false; else
if (!MatchGetEqualityContract(opEqualityCall.Arguments[0], out var target1)) {
return false; // comp.o(ldloc other != ldnull)
if (!MatchGetEqualityContract(opEqualityCall.Arguments[1], out var target2)) if (pos >= conditions.Count)
return false; return false;
if (!target1.MatchLdThis()) if (!conditions[pos].MatchCompNotEqualsNull(out var arg))
return false; return false;
if (!target2.MatchLdLoc(other)) if (!arg.MatchLdLoc(other))
return false; return false;
pos++; pos++;
// call op_Equality(callvirt get_EqualityContract(ldloc this), callvirt get_EqualityContract(ldloc other))
// Special-cased because Roslyn isn't using EqualityComparer<T> here.
if (pos >= conditions.Count)
return false;
if (!(conditions[pos] is Call { Method: { IsOperator: true, Name: "op_Equality" } } opEqualityCall))
return false;
if (!opEqualityCall.Method.DeclaringType.IsKnownType(KnownTypeCode.Type))
return false;
if (opEqualityCall.Arguments.Count != 2)
return false;
if (!MatchGetEqualityContract(opEqualityCall.Arguments[0], out var target1))
return false;
if (!MatchGetEqualityContract(opEqualityCall.Arguments[1], out var target2))
return false;
if (!target1.MatchLdThis())
return false;
if (!target2.MatchLdLoc(other))
return false;
pos++;
}
} }
foreach (var member in orderedMembers) foreach (var member in orderedMembers)
{ {
if (!MemberConsideredForEquality(member)) if (!MemberConsideredForEquality(member))
continue; continue;
if (member.Name == "EqualityContract") if (!isStruct && member.Name == "EqualityContract")
{ {
continue; // already special-cased continue; // already special-cased
} }
@ -771,7 +814,7 @@ namespace ICSharpCode.Decompiler.CSharp
return false; return false;
if (!member1.Equals(member)) if (!member1.Equals(member))
return false; return false;
if (!target2.MatchLdLoc(other)) if (!(isStruct ? target2.MatchLdLoca(other) : target2.MatchLdLoc(other)))
return false; return false;
if (!member2.Equals(member)) if (!member2.Equals(member))
return false; return false;
@ -800,10 +843,12 @@ namespace ICSharpCode.Decompiler.CSharp
} }
} }
private static bool MatchGetEqualityContract(ILInstruction inst, out ILInstruction target) private bool MatchGetEqualityContract(ILInstruction inst, out ILInstruction target)
{ {
target = null; target = null;
if (!(inst is CallVirt { Method: { Name: "get_EqualityContract" } } call)) if (!(inst is CallInstruction { Method: { Name: "get_EqualityContract" } } call))
return false;
if (!(inst is CallVirt || (isSealed && inst is Call)))
return false; return false;
if (call.Arguments.Count != 1) if (call.Arguments.Count != 1)
return false; return false;
@ -830,7 +875,7 @@ namespace ICSharpCode.Decompiler.CSharp
return false; return false;
if (member is IProperty property) if (member is IProperty property)
{ {
if (property.Name == "EqualityContract") if (!isStruct && property.Name == "EqualityContract")
return !isInheritedRecord; return !isInheritedRecord;
return autoPropertyToBackingField.ContainsKey(property); return autoPropertyToBackingField.ContainsKey(property);
} }
@ -944,7 +989,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (!deconstruct.IsOut) if (!deconstruct.IsOut)
return false; return false;
if (!ctor.Type.Equals(((ByReferenceType)deconstruct.Type).ElementType)) IType ctorType = ctor.Type;
if (ctor.IsIn)
ctorType = ((ByReferenceType)ctorType).ElementType;
if (!ctorType.Equals(((ByReferenceType)deconstruct.Type).ElementType))
return false; return false;
if (ctor.Name != deconstruct.Name) if (ctor.Name != deconstruct.Name)
@ -985,14 +1033,14 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
target = null; target = null;
member = null; member = null;
if (inst is CallVirt if (inst is CallInstruction
{ {
Method: Method:
{ {
AccessorKind: System.Reflection.MethodSemanticsAttributes.Getter, AccessorKind: System.Reflection.MethodSemanticsAttributes.Getter,
AccessorOwner: IProperty property AccessorOwner: IProperty property
} }
} call) } call && (call is CallVirt || (isSealed && call is Call)))
{ {
if (call.Arguments.Count != 1) if (call.Arguments.Count != 1)
return false; return false;

2
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -936,7 +936,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
"System.Runtime.CompilerServices.MethodImplAttribute" "System.Runtime.CompilerServices.MethodImplAttribute"
}; };
static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] { internal static readonly string[] attributeTypesToRemoveFromAutoProperties = new[] {
"System.Runtime.CompilerServices.CompilerGeneratedAttribute", "System.Runtime.CompilerServices.CompilerGeneratedAttribute",
"System.Diagnostics.DebuggerBrowsableAttribute" "System.Diagnostics.DebuggerBrowsableAttribute"
}; };

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

@ -118,7 +118,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
&& currentCtor.Equals(record.PrimaryConstructor) && currentCtor.Equals(record.PrimaryConstructor)
&& ci.ConstructorInitializerType == ConstructorInitializerType.Base) && ci.ConstructorInitializerType == ConstructorInitializerType.Base)
{ {
if (constructorDeclaration.Parent is TypeDeclaration { BaseTypes: { Count: >= 1 } } typeDecl) if (record.IsInheritedRecord &&
constructorDeclaration.Parent is TypeDeclaration { BaseTypes: { Count: >= 1 } } typeDecl)
{ {
var baseType = typeDecl.BaseTypes.First(); var baseType = typeDecl.BaseTypes.First();
var newBaseType = new InvocationAstType(); var newBaseType = new InvocationAstType();
@ -176,6 +177,10 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!context.DecompileRun.RecordDecompilers.TryGetValue(ctorMethodDef.DeclaringTypeDefinition, out var record)) if (!context.DecompileRun.RecordDecompilers.TryGetValue(ctorMethodDef.DeclaringTypeDefinition, out var record))
record = null; record = null;
//Filter out copy constructor of records
if (record != null)
instanceCtorsNotChainingWithThis = instanceCtorsNotChainingWithThis.Where(ctor => !record.IsCopyConstructor(ctor.GetSymbol() as IMethod)).ToArray();
// Recognize field or property initializers: // Recognize field or property initializers:
// Translate first statement in all ctors (if all ctors have the same statement) into an initializer. // Translate first statement in all ctors (if all ctors have the same statement) into an initializer.
bool allSame; bool allSame;
@ -201,9 +206,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (initializer.Annotation<ILVariableResolveResult>()?.Variable.Kind == IL.VariableKind.Parameter) if (initializer.Annotation<ILVariableResolveResult>()?.Variable.Kind == IL.VariableKind.Parameter)
{ {
// remove record ctor parameter assignments // remove record ctor parameter assignments
if (IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record)) if (!IsPropertyDeclaredByPrimaryCtor(fieldOrPropertyOrEvent as IProperty, record))
initializer.Remove();
else
break; break;
} }
else else

Loading…
Cancel
Save