Browse Source

Adds support for C# 9 covariant return types in methods and getter-only properties and indexers.

pull/2470/head
Siegfried Pammer 4 years ago
parent
commit
84704a7452
  1. 1
      ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
  2. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  3. 50
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs
  4. 23
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  5. 22
      ICSharpCode.Decompiler/DecompilerSettings.cs
  6. 4
      ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

1
ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj

@ -107,6 +107,7 @@
<Compile Include="TestCases\Correctness\DeconstructionTests.cs" /> <Compile Include="TestCases\Correctness\DeconstructionTests.cs" />
<Compile Include="TestCases\Correctness\DynamicTests.cs" /> <Compile Include="TestCases\Correctness\DynamicTests.cs" />
<Compile Include="TestCases\Correctness\StringConcat.cs" /> <Compile Include="TestCases\Correctness\StringConcat.cs" />
<None Include="TestCases\Pretty\CovariantReturns.cs" />
<Compile Include="TestCases\VBPretty\VBPropertiesTest.cs" /> <Compile Include="TestCases\VBPretty\VBPropertiesTest.cs" />
<None Include="TestCases\ILPretty\Issue2260SwitchString.cs" /> <None Include="TestCases\ILPretty\Issue2260SwitchString.cs" />
<None Include="TestCases\Pretty\Records.cs" /> <None Include="TestCases\Pretty\Records.cs" />

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -573,6 +573,12 @@ namespace ICSharpCode.Decompiler.Tests
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview); RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
} }
[Test]
public void CovariantReturns([ValueSource(nameof(dotnetCoreOnlyOptions))] CompilerOptions cscOptions)
{
RunForLibrary(cscOptions: cscOptions | CompilerOptions.Preview);
}
void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null) void RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, DecompilerSettings decompilerSettings = null)
{ {
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings); Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);

50
ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs

@ -0,0 +1,50 @@
namespace ICSharpCode.Decompiler.Tests.TestCases.CovariantReturns
{
public abstract class Base
{
public abstract Base Instance { get; }
public abstract Base this[int index] { get; }
public virtual Base Build()
{
throw null;
}
protected abstract Base SetParent(object parent);
}
public class Derived : Base
{
public override Derived Instance { get; }
public override Derived this[int index] {
get {
throw null;
}
}
public override Derived Build()
{
throw null;
}
protected override Derived SetParent(object parent)
{
throw null;
}
}
public class UseSites
{
public Base Test(Base x)
{
return x.Build();
}
public Derived Test(Derived x)
{
return x.Build();
}
}
}

23
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -1451,9 +1451,24 @@ namespace ICSharpCode.Decompiler.CSharp
{ {
SetNewModifier(methodDecl); SetNewModifier(methodDecl);
} }
if (IsCovariantReturnOverride(method))
{
RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides);
methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
methodDecl.Modifiers |= Modifiers.Override;
}
return methodDecl; return methodDecl;
} }
private bool IsCovariantReturnOverride(IEntity entity)
{
if (!settings.CovariantReturns)
return false;
if (!entity.HasAttribute(KnownAttribute.PreserveBaseOverrides))
return false;
return true;
}
internal static bool IsWindowsFormsInitializeComponentMethod(IMethod method) internal static bool IsWindowsFormsInitializeComponentMethod(IMethod method)
{ {
return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control"); return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control");
@ -1766,7 +1781,15 @@ namespace ICSharpCode.Decompiler.CSharp
var accessorHandle = (MethodDefinitionHandle)(property.Getter ?? property.Setter).MetadataToken; var accessorHandle = (MethodDefinitionHandle)(property.Getter ?? property.Setter).MetadataToken;
var accessor = metadata.GetMethodDefinition(accessorHandle); var accessor = metadata.GetMethodDefinition(accessorHandle);
if (!accessorHandle.GetMethodImplementations(metadata).Any() && accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot)) if (!accessorHandle.GetMethodImplementations(metadata).Any() && accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot))
{
SetNewModifier(propertyDecl); SetNewModifier(propertyDecl);
}
if (IsCovariantReturnOverride(property.Getter))
{
RemoveAttribute(getter, KnownAttribute.PreserveBaseOverrides);
propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
propertyDecl.Modifiers |= Modifiers.Override;
}
return propertyDecl; return propertyDecl;
} }
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException)) catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))

22
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -139,12 +139,14 @@ namespace ICSharpCode.Decompiler
recordClasses = false; recordClasses = false;
withExpressions = false; withExpressions = false;
usePrimaryConstructorSyntax = false; usePrimaryConstructorSyntax = false;
covariantReturns = false;
} }
} }
public CSharp.LanguageVersion GetMinimumRequiredVersion() public CSharp.LanguageVersion GetMinimumRequiredVersion()
{ {
if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension || recordClasses) if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension
|| recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns)
return CSharp.LanguageVersion.Preview; return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions) || staticLocalFunctions || ranges || switchExpressions)
@ -193,6 +195,24 @@ namespace ICSharpCode.Decompiler
} }
} }
bool covariantReturns = true;
/// <summary>
/// Decompile C# 9 covariant return types.
/// </summary>
[Category("C# 9.0 / VS 2019.8")]
[Description("DecompilerSettings.CovariantReturns")]
public bool CovariantReturns {
get { return covariantReturns; }
set {
if (covariantReturns != value)
{
covariantReturns = value;
OnPropertyChanged();
}
}
}
bool initAccessors = true; bool initAccessors = true;
/// <summary> /// <summary>

4
ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs

@ -102,11 +102,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes: // C# 9 attributes:
NativeInteger, NativeInteger,
PreserveBaseOverrides,
} }
static class KnownAttributes static class KnownAttributes
{ {
internal const int Count = (int)KnownAttribute.NativeInteger + 1; internal const int Count = (int)KnownAttribute.PreserveBaseOverrides + 1;
static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{ static readonly TopLevelTypeName[] typeNames = new TopLevelTypeName[Count]{
default, default,
@ -167,6 +168,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Security.Permissions", "PermissionSetAttribute"), new TopLevelTypeName("System.Security.Permissions", "PermissionSetAttribute"),
// C# 9 attributes: // C# 9 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"), new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"),
new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
}; };
public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr) public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)

Loading…
Cancel
Save