diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index 65c70668a8..b1d70dfda3 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -99,6 +99,7 @@
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 07a3fa84e8..3bce841c01 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -569,6 +569,12 @@ public void CS9_ExtensionGetEnumerator([ValueSource(nameof(dotnetCoreOnlyOptions
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 0000000000..11987dde5c
--- /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 4fc6c16432..ccad740d69 100644
--- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
+++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
@@ -1215,9 +1215,24 @@ EntityDeclaration DoDecompile(Decompiler.TypeSystem.IMethod method, DecompileRun
if (method.SymbolKind == SymbolKind.Method && !method.IsExplicitInterfaceImplementation && methodDefinition.IsVirtual == methodDefinition.IsNewSlot) {
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(ICSharpCode.Decompiler.TypeSystem.IMethod method)
{
return method.ReturnType.Kind == TypeKind.Void && method.Name == "InitializeComponent" && method.DeclaringTypeDefinition.GetNonInterfaceBaseTypes().Any(t => t.FullName == "System.Windows.Forms.Control");
@@ -1470,7 +1485,15 @@ EntityDeclaration DoDecompile(IProperty property, DecompileRun decompileRun, ITy
}
var accessor = (MethodDef)(property.Getter ?? property.Setter).MetadataToken;
if (!accessor.HasOverrides && accessor.IsVirtual == accessor.IsNewSlot)
+ {
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)) {
throw new DecompilerException(property.MetadataToken, innerException);
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
index 7fa9cfcb31..3dec9c5da8 100644
--- a/ICSharpCode.Decompiler/DecompilerSettings.cs
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -127,12 +127,14 @@ public void SetLanguageVersion(CSharp.LanguageVersion languageVersion)
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)
return CSharp.LanguageVersion.CSharp8_0;
@@ -175,6 +177,24 @@ public bool NativeIntegers {
}
}
+ 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 d45109c934..b6be4954a0 100644
--- a/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
+++ b/ICSharpCode.Decompiler/TypeSystem/Implementation/KnownAttributes.cs
@@ -102,11 +102,12 @@ public enum KnownAttribute
// 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 @@ static class KnownAttributes
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)