Browse Source

Records: support generic records

pull/2251/head
Daniel Grunwald 4 years ago
parent
commit
d9874380cd
  1. 18
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Records.cs
  2. 19
      ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs
  3. 2
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  4. 17
      ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

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

@ -13,6 +13,18 @@ @@ -13,6 +13,18 @@
public string S = "abc";
}
public record Pair<A, B>
{
public A First {
get;
init;
}
public B Second {
get;
init;
}
}
public record Properties
{
public int A {
@ -42,3 +54,9 @@ @@ -42,3 +54,9 @@
}
}
}
namespace System.Runtime.CompilerServices
{
internal class IsExternalInit
{
}
}

19
ICSharpCode.Decompiler/CSharp/RecordDecompiler.cs

@ -53,9 +53,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -53,9 +53,11 @@ namespace ICSharpCode.Decompiler.CSharp
void DetectAutomaticProperties()
{
foreach (var p in recordTypeDef.Properties)
var subst = recordTypeDef.AsParameterizedType().GetSubstitution();
foreach (var property in recordTypeDef.Properties)
{
cancellationToken.ThrowIfCancellationRequested();
var p = (IProperty)property.Specialize(subst);
if (IsAutoProperty(p, out var field))
{
backingFieldToAutoProperty.Add(field, p);
@ -152,8 +154,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -152,8 +154,9 @@ namespace ICSharpCode.Decompiler.CSharp
// need to detect the correct interleaving.
// We could try to detect this from the PrintMembers body, but let's initially
// restrict ourselves to the common case where the record only uses properties.
return recordTypeDef.Properties.Concat<IMember>(
recordTypeDef.Fields.Where(f => !backingFieldToAutoProperty.ContainsKey(f))
var subst = recordTypeDef.AsParameterizedType().GetSubstitution();
return recordTypeDef.Properties.Select(p => p.Specialize(subst)).Concat(
recordTypeDef.Fields.Select(f => (IField)f.Specialize(subst)).Where(f => !backingFieldToAutoProperty.ContainsKey(f))
).ToList();
}
@ -417,9 +420,15 @@ namespace ICSharpCode.Decompiler.CSharp @@ -417,9 +420,15 @@ namespace ICSharpCode.Decompiler.CSharp
val = addressOf.Value;
}
}
else if (val is Box box)
{
if (!NormalizeTypeVisitor.TypeErasure.EquivalentTypes(box.Type, member.ReturnType))
return false;
val = box.Argument;
}
if (val is CallInstruction getterCall && member is IProperty property)
{
if (!getterCall.Method.MemberDefinition.Equals(property.Getter.MemberDefinition))
if (!getterCall.Method.Equals(property.Getter))
return false;
if (getterCall.Arguments.Count != 1)
return false;
@ -430,7 +439,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -430,7 +439,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
if (!target.MatchLdThis())
return false;
if (!field.MemberDefinition.Equals(member))
if (!field.Equals(member))
return false;
}
else

2
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -1804,7 +1804,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -1804,7 +1804,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
else if (SupportRecordClasses && typeDefinition.IsRecord
&& baseType.Name == "IEquatable" && baseType.Namespace == "System"
&& baseType.TypeArguments.Count == 1
&& baseType.TypeArguments[0].Equals(typeDefinition))
&& baseType.TypeArguments[0].Equals(typeDefinition.AsParameterizedType()))
{
// omit "IEquatable<R>" in records
continue;

17
ICSharpCode.Decompiler/TypeSystem/TypeSystemExtensions.cs

@ -616,5 +616,22 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -616,5 +616,22 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
return null;
}
/// <summary>
/// When given a generic type definition, returns the self-parameterized type
/// (i.e. the type of "this" within the type definition).
/// When given a non-generic type definition, returns that definition unchanged.
/// </summary>
public static IType AsParameterizedType(this ITypeDefinition td)
{
if (td.TypeParameterCount == 0)
{
return td;
}
else
{
return new ParameterizedType(td, td.TypeArguments);
}
}
}
}

Loading…
Cancel
Save