diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index e0334ffcb..71edc5762 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -107,6 +107,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 49d82222c..30ee7b18d 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -573,6 +573,12 @@ namespace ICSharpCode.Decompiler.Tests
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)
{
Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, decompilerSettings);
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/CovariantReturns.cs
new file mode 100644
index 000000000..11987dde5
--- /dev/null
+++ b/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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
index 676c924d4..fe1ea0ee9 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -1451,9 +1451,24 @@ namespace ICSharpCode.Decompiler.CSharp
{
SetNewModifier(methodDecl);
}
+ if (IsCovariantReturnOverride(method))
+ {
+ RemoveAttribute(methodDecl, KnownAttribute.PreserveBaseOverrides);
+ methodDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
+ methodDecl.Modifiers |= Modifiers.Override;
+ }
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)
{
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 accessor = metadata.GetMethodDefinition(accessorHandle);
if (!accessorHandle.GetMethodImplementations(metadata).Any() && accessor.HasFlag(System.Reflection.MethodAttributes.Virtual) == accessor.HasFlag(System.Reflection.MethodAttributes.NewSlot))
+ {
SetNewModifier(propertyDecl);
+ }
+ if (IsCovariantReturnOverride(property.Getter))
+ {
+ RemoveAttribute(getter, KnownAttribute.PreserveBaseOverrides);
+ propertyDecl.Modifiers &= ~(Modifiers.New | Modifiers.Virtual);
+ propertyDecl.Modifiers |= Modifiers.Override;
+ }
return propertyDecl;
}
catch (Exception innerException) when (!(innerException is OperationCanceledException || innerException is DecompilerException))
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index b7c467223..b456b3203 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -139,12 +139,14 @@ namespace ICSharpCode.Decompiler
recordClasses = false;
withExpressions = false;
usePrimaryConstructorSyntax = false;
+ covariantReturns = false;
}
}
public CSharp.LanguageVersion GetMinimumRequiredVersion()
{
- if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension || recordClasses)
+ if (nativeIntegers || initAccessors || functionPointers || forEachWithGetEnumeratorExtension
+ || recordClasses || withExpressions || usePrimaryConstructorSyntax || covariantReturns)
return CSharp.LanguageVersion.Preview;
if (nullableReferenceTypes || readOnlyMethods || asyncEnumerator || asyncUsingAndForEachStatement
|| staticLocalFunctions || ranges || switchExpressions)
@@ -193,6 +195,24 @@ namespace ICSharpCode.Decompiler
}
}
+ bool covariantReturns = true;
+
+ ///
+ /// Decompile C# 9 covariant return types.
+ ///
+ [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;
///
diff --git a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
index aae66c064..2ce7b2b62 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
@@ -102,11 +102,12 @@ namespace ICSharpCode.Decompiler.TypeSystem
// C# 9 attributes:
NativeInteger,
+ PreserveBaseOverrides,
}
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]{
default,
@@ -167,6 +168,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
new TopLevelTypeName("System.Security.Permissions", "PermissionSetAttribute"),
// C# 9 attributes:
new TopLevelTypeName("System.Runtime.CompilerServices", "NativeIntegerAttribute"),
+ new TopLevelTypeName("System.Runtime.CompilerServices", "PreserveBaseOverridesAttribute"),
};
public static ref readonly TopLevelTypeName GetTypeName(this KnownAttribute attr)