diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
index 3d6a155fa17e9..280f1908f79f0 100644
--- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs
@@ -35,9 +35,7 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reflection.Metadata.Ecma335;
using System.Reflection.Runtime.TypeParsing;
-using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
using ILCompiler.DependencyAnalysisFramework;
using ILLink.Shared;
@@ -718,33 +716,25 @@ void MarkMethodIfNeededByBaseMethod (MethodDefinition method, MessageOrigin orig
/// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked
///
bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefinition interfaceType)
+ => IsInterfaceImplementationMarkedRecursively (type, interfaceType, Context);
+
+ ///
+ /// Returns true if implements and the interface implementation is marked,
+ /// or if any marked interface implementations on are interfaces that implement and that interface implementation is marked
+ ///
+ internal static bool IsInterfaceImplementationMarkedRecursively (TypeDefinition type, TypeDefinition interfaceType, LinkContext context)
{
if (type.HasInterfaces) {
foreach (var intf in type.Interfaces) {
- TypeDefinition? resolvedInterface = Context.Resolve (intf.InterfaceType);
+ TypeDefinition? resolvedInterface = context.Resolve (intf.InterfaceType);
if (resolvedInterface == null)
continue;
-
- if (Annotations.IsMarked (intf) && RequiresInterfaceRecursively (resolvedInterface, interfaceType))
- return true;
- }
- }
-
- return false;
- }
-
- bool RequiresInterfaceRecursively (TypeDefinition typeToExamine, TypeDefinition interfaceType)
- {
- if (typeToExamine == interfaceType)
- return true;
-
- if (typeToExamine.HasInterfaces) {
- foreach (var iface in typeToExamine.Interfaces) {
- var resolved = Context.TryResolve (iface.InterfaceType);
- if (resolved == null)
+ if (!context.Annotations.IsMarked (intf))
continue;
- if (RequiresInterfaceRecursively (resolved, interfaceType))
+ if (resolvedInterface == interfaceType)
+ return true;
+ if (IsInterfaceImplementationMarkedRecursively (resolvedInterface, interfaceType, context))
return true;
}
}
@@ -3197,8 +3187,12 @@ protected virtual void ProcessMethod (MethodDefinition method, in DependencyInfo
Context.Resolve (@base) is MethodDefinition baseDefinition
&& baseDefinition.DeclaringType.IsInterface && baseDefinition.IsStatic && method.IsStatic)
continue;
+ // Instance methods can have overrides on public implementation methods in IL, but C# will usually only have them for private explicit interface implementations.
+ // It is valid IL for a public method to override an interface method and only be called directly. In this case it would be safe to skip marking the .override method.
+ // However, in most cases, the C# compiler will only generate .override for instance methods when it's a private explicit interface implementations which can only be called through the interface.
+ // We can just take a short cut and mark all the overrides on instance methods. We shouldn't miss out on size savings for code generated by Roslyn.
MarkMethod (@base, new DependencyInfo (DependencyKind.MethodImplOverride, method), methodOrigin);
- MarkExplicitInterfaceImplementation (method, @base);
+ MarkRuntimeInterfaceImplementation (method, @base);
}
}
@@ -3314,22 +3308,21 @@ protected virtual void MarkRequirementsForInstantiatedTypes (TypeDefinition type
DoAdditionalInstantiatedTypeProcessing (type);
}
- void MarkExplicitInterfaceImplementation (MethodDefinition method, MethodReference ov)
+ void MarkRuntimeInterfaceImplementation (MethodDefinition method, MethodReference ov)
{
if (Context.Resolve (ov) is not MethodDefinition resolvedOverride)
return;
+ if (!resolvedOverride.DeclaringType.IsInterface)
+ return;
+ var interfaceToBeImplemented = ov.DeclaringType;
- if (resolvedOverride.DeclaringType.IsInterface) {
- foreach (var ifaceImpl in method.DeclaringType.Interfaces) {
- var resolvedInterfaceType = Context.Resolve (ifaceImpl.InterfaceType);
- if (resolvedInterfaceType == null) {
- continue;
- }
-
- if (resolvedInterfaceType == resolvedOverride.DeclaringType) {
- MarkInterfaceImplementation (ifaceImpl, new MessageOrigin (method.DeclaringType));
- return;
- }
+ var ifaces = Annotations.GetRecursiveInterfaces (method.DeclaringType);
+ if (ifaces is null)
+ return;
+ foreach (var iface in ifaces) {
+ if (TypeReferenceEqualityComparer.AreEqual (iface.InterfaceType, interfaceToBeImplemented, Context)) {
+ MarkInterfaceImplementationList (iface.ImplementationChain, new MessageOrigin (method.DeclaringType));
+ return;
}
}
}
diff --git a/src/tools/illink/src/linker/Linker.Steps/SweepStep.cs b/src/tools/illink/src/linker/Linker.Steps/SweepStep.cs
index 794ef60ea19a7..eb3a8fa0f55fc 100644
--- a/src/tools/illink/src/linker/Linker.Steps/SweepStep.cs
+++ b/src/tools/illink/src/linker/Linker.Steps/SweepStep.cs
@@ -456,14 +456,19 @@ void SweepOverrides (MethodDefinition method)
// This can happen for a couple of reasons, but it indicates the method isn't in the final assembly.
// Resolve also may return a removed value if method.Overrides[i] is a MethodDefinition. In this case, Resolve short circuits and returns `this`.
// OR
- // ov.DeclaringType is null
+ // ov.DeclaringType is null
// ov.DeclaringType may be null if Resolve short circuited and returned a removed method. In this case, we want to remove the override.
// OR
- // ov is in a `link` scope and is unmarked
+ // ov is in a `link` scope and is unmarked
// ShouldRemove returns true if the method is unmarked, but we also We need to make sure the override is in a link scope.
// Only things in a link scope are marked, so ShouldRemove is only valid for items in a `link` scope.
+ // OR
+ // ov is an interface method and the interface is not implemented by the type
#pragma warning disable RS0030 // Cecil's Resolve is banned - it's necessary when the metadata graph isn't stable
- if (method.Overrides[i].Resolve () is not MethodDefinition ov || ov.DeclaringType is null || (IsLinkScope (ov.DeclaringType.Scope) && ShouldRemove (ov)))
+ if (method.Overrides[i].Resolve () is not MethodDefinition ov
+ || ov.DeclaringType is null
+ || (IsLinkScope (ov.DeclaringType.Scope) && ShouldRemove (ov))
+ || (ov.DeclaringType.IsInterface && !MarkStep.IsInterfaceImplementationMarkedRecursively (method.DeclaringType, ov.DeclaringType, Context)))
method.Overrides.RemoveAt (i);
else
i++;
diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/StaticInterfaceMethods.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/StaticInterfaceMethods.cs
index 77e6421a9572d..747c03965d5eb 100644
--- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/StaticInterfaceMethods.cs
+++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/StaticInterfaceMethods.cs
@@ -16,6 +16,12 @@ public Task BaseProvidesInterfaceMethod ()
return RunTest (allowMissingWarnings: false);
}
+ [Fact]
+ public Task RemovedInterfaceImplementationRemovedOverride()
+ {
+ return RunTest (allowMissingWarnings: false);
+ }
+
[Fact]
public Task StaticAbstractInterfaceMethods ()
{
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideAttribute.cs
index 3c02278ea9142..919e62ed423a4 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideAttribute.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideAttribute.cs
@@ -15,12 +15,12 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class KeptOverrideAttribute : KeptAttribute
{
- public Type TypeWithOverriddenMethodDeclaration;
-
public KeptOverrideAttribute (Type typeWithOverriddenMethod)
{
ArgumentNullException.ThrowIfNull (typeWithOverriddenMethod);
- TypeWithOverriddenMethodDeclaration = typeWithOverriddenMethod;
+ }
+ public KeptOverrideAttribute (string typeWithOverriddenMethod)
+ {
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideOnMethodInAssemblyAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideOnMethodInAssemblyAttribute.cs
new file mode 100644
index 0000000000000..22943070f94ad
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptOverrideOnMethodInAssemblyAttribute.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.All, AllowMultiple = true)]
+ public class KeptOverrideOnMethodInAssemblyAttribute : BaseInAssemblyAttribute
+ {
+ public KeptOverrideOnMethodInAssemblyAttribute (string assemblyName, string typeName, string methodName, string overriddenMethodName)
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideAttribute.cs
index c15cf9a9e4148..99deecc3b7c25 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideAttribute.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideAttribute.cs
@@ -14,12 +14,14 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions
[AttributeUsage (AttributeTargets.Method, AllowMultiple = true, Inherited = false)]
public class RemovedOverrideAttribute : BaseInAssemblyAttribute
{
- public Type TypeWithOverriddenMethodDeclaration;
public RemovedOverrideAttribute (Type typeWithOverriddenMethod)
{
if (typeWithOverriddenMethod == null)
throw new ArgumentException ("Value cannot be null or empty.", nameof (typeWithOverriddenMethod));
- TypeWithOverriddenMethodDeclaration = typeWithOverriddenMethod;
+ }
+
+ public RemovedOverrideAttribute (string nameOfOverriddenMethod)
+ {
}
}
-}
\ No newline at end of file
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideOnMethodInAssemblyAttribute.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideOnMethodInAssemblyAttribute.cs
new file mode 100644
index 0000000000000..776a496cfa60a
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations/Assertions/RemovedOverrideOnMethodInAssemblyAttribute.cs
@@ -0,0 +1,15 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace Mono.Linker.Tests.Cases.Expectations.Assertions
+{
+ [AttributeUsage (AttributeTargets.All, AllowMultiple = true, Inherited = false)]
+ public class RemovedOverrideOnMethodInAssemblyAttribute : BaseInAssemblyAttribute
+ {
+ public RemovedOverrideOnMethodInAssemblyAttribute (string library, string typeName, string methodName, string overriddenMethodFullName)
+ {
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
index bff8cbfe57acc..12ff0b8ca3694 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/FeatureCheckDataFlow.cs
@@ -21,7 +21,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
// not as a separate substitution file, for it to work with NativeAot.
// Related: https://github.com/dotnet/runtime/issues/88647
[SetupCompileBefore ("TestFeatures.dll", new[] { "Dependencies/TestFeatures.cs" },
- resources: new object[] { new [] { "FeatureCheckDataFlowTestSubstitutions.xml", "ILLink.Substitutions.xml" } })]
+ resources: new object[] { new[] { "FeatureCheckDataFlowTestSubstitutions.xml", "ILLink.Substitutions.xml" } })]
[IgnoreSubstitutions (false)]
public class FeatureCheckDataFlow
{
@@ -66,12 +66,9 @@ static void UnguardedIf ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")]
static void UnguardedElse ()
{
- if (TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (TestFeatures.IsUnreferencedCodeSupported) {
throw new Exception ();
- }
- else
- {
+ } else {
RequiresUnreferencedCode ();
RequiresDynamicCode ();
RequiresAssemblyFiles ();
@@ -103,8 +100,7 @@ static void UnguardedTernary ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")]
static void UnguardedThrow ()
{
- if (TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (TestFeatures.IsUnreferencedCodeSupported) {
throw new Exception ();
}
@@ -118,8 +114,7 @@ static void UnguardedThrow ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer | Tool.NativeAot, "NativeAOT Specific Warning")]
static void UnguardedReturn ()
{
- if (TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (TestFeatures.IsUnreferencedCodeSupported) {
return;
}
@@ -220,12 +215,9 @@ static void GuardedIf ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")]
static void GuardedElse ()
{
- if (!TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (!TestFeatures.IsUnreferencedCodeSupported) {
throw new Exception ();
- }
- else
- {
+ } else {
RequiresUnreferencedCode ();
RequiresDynamicCode ();
RequiresAssemblyFiles ();
@@ -252,8 +244,7 @@ static void GuardedTernary ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")]
static void GuardedThrow ()
{
- if (!TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (!TestFeatures.IsUnreferencedCodeSupported) {
throw new Exception ();
}
@@ -266,8 +257,7 @@ static void GuardedThrow ()
[ExpectedWarning ("IL3002", nameof (RequiresAssemblyFiles), Tool.Analyzer, "Trimmer and NativeAOT eliminate the entire problematic branch. Analyzer can only guarantee UnreferencedCode will be supported")]
static void GuardedReturn ()
{
- if (!TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (!TestFeatures.IsUnreferencedCodeSupported) {
return;
}
@@ -696,8 +686,7 @@ class ExceptionalDataFlow
static void GuardedTryCatchFinally ()
{
- if (TestFeatures.IsUnreferencedCodeSupported)
- {
+ if (TestFeatures.IsUnreferencedCodeSupported) {
try {
RequiresUnreferencedCode0 ();
} catch {
@@ -788,7 +777,8 @@ static void NestedTryInCheckInFinally ()
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))]
// Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
- static void AssertInTryNoCatch () {
+ static void AssertInTryNoCatch ()
+ {
try {
Debug.Assert (TestFeatures.IsUnreferencedCodeSupported);
} finally {
@@ -800,7 +790,8 @@ static void AssertInTryNoCatch () {
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2))]
- static void AssertInTryWithCatch () {
+ static void AssertInTryWithCatch ()
+ {
try {
Debug.Assert (TestFeatures.IsUnreferencedCodeSupported);
} catch {
@@ -814,7 +805,8 @@ static void AssertInTryWithCatch () {
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode0))]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2))]
- static void AssertInCatch () {
+ static void AssertInCatch ()
+ {
try {
RequiresUnreferencedCode0 ();
} catch {
@@ -829,7 +821,8 @@ static void AssertInCatch () {
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1))]
// Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode2), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
- static void AssertInFinally () {
+ static void AssertInFinally ()
+ {
try {
RequiresUnreferencedCode0 ();
} catch {
@@ -927,7 +920,8 @@ static void AssertInTryWithCatchNestedInFinally ()
// Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
- static void AssertInFinallyNestedInTry () {
+ static void AssertInFinallyNestedInTry ()
+ {
try {
try {
RequiresUnreferencedCode0 ();
@@ -946,7 +940,8 @@ static void AssertInFinallyNestedInTry () {
// Trimmer/NativeAot don't optimize branches away based on DoesNotReturnIfAttribute
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode1), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
[ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode3), Tool.Trimmer | Tool.NativeAot, "ILLink and NativeAOT should not respect DoesNotReturnAttribute")]
- static void AssertInFinallyWithCatchNestedInTry () {
+ static void AssertInFinallyWithCatchNestedInTry ()
+ {
try {
try {
RequiresUnreferencedCode0 ();
@@ -1021,7 +1016,8 @@ static void AssertInTryWithTryFinallyInFinally ()
}
}
- public static void Test () {
+ public static void Test ()
+ {
GuardedTryCatchFinally ();
CheckInTry ();
NestedTryInCheckInTry ();
@@ -1056,7 +1052,7 @@ static IEnumerable GuardInIterator ()
}
}
- [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer, "https://github.com/dotnet/linker/issues/3087", CompilerGeneratedCode = true)]
+ [ExpectedWarning ("IL2026", nameof (RequiresUnreferencedCode), Tool.Trimmer | Tool.NativeAot, "https://github.com/dotnet/linker/issues/3087", CompilerGeneratedCode = true)]
static IEnumerable StateFlowsAcrossYield ()
{
if (!TestFeatures.IsUnreferencedCodeSupported)
@@ -1133,7 +1129,7 @@ static void GuardedLambda ()
if (TestFeatures.IsUnreferencedCodeSupported) {
a = [RequiresUnreferencedCode (nameof (RequiresUnreferencedCode))]
- () => RequiresUnreferencedCode ();
+ () => RequiresUnreferencedCode ();
}
if (TestFeatures.IsUnreferencedCodeSupported) {
@@ -1153,7 +1149,7 @@ static void GuardedLocalFunction ()
public static void Test ()
{
// Use the IEnumerable to mark the IEnumerable methods
- GuardInIterator ();
+ foreach (var x in GuardInIterator ()) ;
StateFlowsAcrossYield ();
GuardInAsync ();
StateFlowsAcrossAwait ();
@@ -1167,45 +1163,45 @@ public static void Test ()
}
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode))]
- static void RequiresUnreferencedCode () {}
+ static void RequiresUnreferencedCode () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode0))]
- static void RequiresUnreferencedCode0 () {}
+ static void RequiresUnreferencedCode0 () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode1))]
- static void RequiresUnreferencedCode1 () {}
+ static void RequiresUnreferencedCode1 () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode2))]
- static void RequiresUnreferencedCode2 () {}
+ static void RequiresUnreferencedCode2 () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode3))]
- static void RequiresUnreferencedCode3 () {}
+ static void RequiresUnreferencedCode3 () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCode4))]
- static void RequiresUnreferencedCode4 () {}
+ static void RequiresUnreferencedCode4 () { }
[RequiresUnreferencedCode (nameof (RequiresUnreferencedCodeBool))]
static bool RequiresUnreferencedCodeBool () => true;
[RequiresDynamicCode (nameof (RequiresUnreferencedCode))]
- static void RequiresDynamicCode () {}
+ static void RequiresDynamicCode () { }
[RequiresAssemblyFiles (nameof (RequiresAssemblyFiles))]
- static void RequiresAssemblyFiles () {}
+ static void RequiresAssemblyFiles () { }
- static void DoesNotReturnIfTrue ([DoesNotReturnIf (true)] bool condition) {}
+ static void DoesNotReturnIfTrue ([DoesNotReturnIf (true)] bool condition) { }
- static void DoesNotReturnIfFalse ([DoesNotReturnIf (false)] bool condition) {}
+ static void DoesNotReturnIfFalse ([DoesNotReturnIf (false)] bool condition) { }
class DoesNotReturnIfFalseCtor
{
- public DoesNotReturnIfFalseCtor ([DoesNotReturnIf (false)] bool condition) {}
+ public DoesNotReturnIfFalseCtor ([DoesNotReturnIf (false)] bool condition) { }
}
[DoesNotReturn]
- static void DoesNotReturn() {}
+ static void DoesNotReturn () { }
- static void RequiresAll([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) {}
+ static void RequiresAll ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) { }
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
static Type RequiresAllField;
@@ -1216,6 +1212,6 @@ class ClassWithRequires
public static int StaticField = 0;
}
- class RequiresAllGeneric<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> {}
+ class RequiresAllGeneric<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] T> { }
}
}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/OverrideOfRecursiveInterfaceIsRemoved.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/OverrideOfRecursiveInterfaceIsRemoved.il
new file mode 100644
index 0000000000000..43af0e6b95792
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/Dependencies/OverrideOfRecursiveInterfaceIsRemoved.il
@@ -0,0 +1,168 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+.assembly extern mscorlib { }
+
+.assembly 'library' { }
+
+.class public auto ansi beforefieldinit Program
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class interface nested public auto ansi abstract beforefieldinit IBaseUnused
+ {
+ // Methods
+ .method public hidebysig abstract virtual static
+ void M () cil managed
+ {
+ } // end of method IBaseUnused::M
+
+ } // end of class IBaseUnused
+
+ .class interface nested public auto ansi abstract beforefieldinit IBaseUsed
+ {
+ // Methods
+ .method public hidebysig abstract virtual static
+ void M () cil managed
+ {
+ } // end of method IBaseUsed::M
+
+ } // end of class IBaseUsed
+
+ .class interface nested private auto ansi abstract beforefieldinit IMiddleUnused
+ implements Program/IBaseUnused,
+ Program/IBaseUsed
+ {
+ // Methods
+ .method public hidebysig abstract virtual static
+ void O () cil managed
+ {
+ } // end of method IMiddleUnused::O
+
+ } // end of class IMiddleUnused
+
+ .class interface nested private auto ansi abstract beforefieldinit IDerived
+ implements Program/IMiddleUnused
+ {
+ // Methods
+ .method public hidebysig abstract virtual static
+ void N () cil managed
+ {
+ } // end of method IDerived::N
+
+ } // end of class IDerived
+
+ .class nested private auto ansi beforefieldinit A
+ extends [mscorlib]System.Object
+ implements Program/IDerived
+ {
+ // Methods
+ .method public hidebysig static
+ void M () cil managed
+ {
+ .override method void Program/IBaseUnused::M()
+ .override method void Program/IBaseUsed::M()
+ // Method begins at RVA 0x2083
+ // Code size 1 (0x1)
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method A::M
+
+ .method public hidebysig static
+ void N () cil managed
+ {
+ .override method void Program/IDerived::N()
+ // Method begins at RVA 0x2083
+ // Code size 1 (0x1)
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method A::N
+
+ .method public hidebysig static
+ void O () cil managed
+ {
+ .override method void Program/IMiddleUnused::O()
+ // Method begins at RVA 0x2083
+ // Code size 1 (0x1)
+ .maxstack 8
+
+ IL_0000: ret
+ } // end of method A::O
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x207b
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method A::.ctor
+
+ } // end of class A
+
+
+ // Methods
+ .method public hidebysig static
+ void MyTest () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Code size 16 (0x10)
+ .maxstack 8
+
+ IL_0000: call void Program::UseNThroughIDerived()
+ IL_0005: call void Program::UseMThroughIDerived()
+ IL_000a: call void Program/A::M()
+ IL_000f: call void Program/A::O()
+ IL_0014: ret
+ } // end of method Program::MyTest
+
+ .method private hidebysig static
+ void UseNThroughIDerived<(Program/IDerived) T> () cil managed
+ {
+ .param constraint T, Program/IDerived
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ // Method begins at RVA 0x2061
+ // Code size 12 (0xc)
+ .maxstack 8
+
+ IL_0000: constrained. !!T
+ IL_0006: call void Program/IDerived::N()
+ IL_000b: ret
+ } // end of method Program::UseNThroughIDerived
+
+ .method private hidebysig static
+ void UseMThroughIDerived<(Program/IBaseUsed) T> () cil managed
+ {
+ .param constraint T, Program/IBaseUnused
+ .custom instance void [mscorlib]System.Runtime.CompilerServices.NullableAttribute::.ctor(uint8) = (
+ 01 00 01 00 00
+ )
+ // Method begins at RVA 0x206e
+ // Code size 12 (0xc)
+ .maxstack 8
+
+ IL_0000: constrained. !!T
+ IL_0006: call void Program/IBaseUsed::M()
+ IL_000b: ret
+ } // end of method Program::UseMThroughIDerived
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x207b
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method Program::.ctor
+
+} // end of class Program
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
index bb0d318cac1a1..6e703f79d4650 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/GenericInterfaceImplementedRecursively.cs
@@ -19,18 +19,18 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
#if IL_ASSEMBLY_AVAILABLE
[KeptTypeInAssembly ("library.dll", typeof(Program.IBase<>))]
[KeptTypeInAssembly ("library.dll", typeof(Program.IMiddle<>))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IMiddle<>), "library.dll", typeof (Program.IBase<>))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddle`1", "library.dll", "Program/IBase`1")]
[KeptTypeInAssembly ("library.dll", typeof(Program.IDerived<>))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.IDerived<>), "library.dll", typeof (Program.IMiddle<>))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived`1", "library.dll", "Program/IMiddle`1")]
[KeptTypeInAssembly ("library.dll", typeof(Program.C))]
- [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (Program.C), "library.dll", typeof (Program.IDerived))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/C", "library.dll", "Program/IDerived`1")]
#endif
///
/// This test case is to verify that the linker will keep all the metadata necessary for C to implement IBase when an interfaceImpl isn't directly on C.
///
class GenericInterfaceImplementedRecursively
{
- public static void Main()
+ public static void Main ()
{
#if IL_ASSEMBLY_AVAILABLE
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs
index 89f59777c5fcd..ce0995821a254 100644
--- a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/InterfaceImplementedRecursively.cs
@@ -30,13 +30,12 @@ namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
///
class InterfaceImplementedRecursively
{
- public static void Main()
+ public static void Main ()
{
#if IL_ASSEMBLY_AVAILABLE
Program.IBase b = null;
object c = new Program.C();
-
#endif
}
}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs
new file mode 100644
index 0000000000000..4beafaa1d9d96
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/RecursiveInterfaces/OverrideOfRecursiveInterfaceIsRemoved.cs
@@ -0,0 +1,87 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.RecursiveInterfaces
+{
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new string[] { "Dependencies/OverrideOfRecursiveInterfaceIsRemoved.il" })]
+ [Kept]
+ [KeptTypeInAssembly ("library.dll", "Program/A")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/A", "library.dll", "Program/IDerived")]
+ [KeptTypeInAssembly ("library.dll", "Program/IDerived")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IDerived", "library.dll", "Program/IMiddleUnused")]
+ [KeptTypeInAssembly ("library.dll", "Program/IMiddleUnused")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddleUnused", "library.dll", "Program/IBaseUsed")]
+ [KeptTypeInAssembly ("library.dll", "Program/IBaseUsed")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "Program/IMiddleUnused", "library.dll", "Program/IBaseUnused")]
+ [KeptTypeInAssembly ("library.dll", "Program/IBaseUnused")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "Program/A", "N", "System.Void Program/IDerived::N()")]
+ [KeptOverrideOnMethodInAssembly ("library.dll", "Program/A", "M", "System.Void Program/IBaseUsed::M()")]
+ [RemovedOverrideOnMethodInAssembly ("library.dll", "Program/A", "M", "System.Void Program/IBaseUnused::M()")]
+ [RemovedOverrideOnMethodInAssembly ("library.dll", "Program/A", "O", "System.Void Program/IMiddleUnused::O()")]
+ [RemovedMemberInAssembly ("library.dll", "Program/IBaseUnused", "M()")]
+ [RemovedMemberInAssembly ("library.dll", "Program/IMiddleUnused", "O()")]
+ public class OverrideOfRecursiveInterfaceIsRemoved
+ {
+ [Kept]
+ public static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ Program.MyTest();
+ _ = typeof(Program.IBaseUnused);
+#endif
+ }
+ }
+ //public class Program
+ //{
+ // public static void MyTest()
+ // {
+ // UseNThroughIDerived();
+ // A.M();
+ // A.O();
+ // }
+
+ // static void UseNThroughIDerived() where T : IDerived {
+ // T.N();
+ // }
+
+ // static void UseMThroughIDerived() where T : IBaseUsed {
+ // T.M();
+ // }
+
+ // interface IBaseUnused
+ // {
+ // static abstract void M();
+ // }
+
+ // interface IBaseUsed
+ // {
+ // static abstract void M();
+ // }
+
+ // interface IMiddleUnused : IBaseUnused, IBaseUsed
+ // {
+ // static abstract void O();
+ // }
+
+ // interface IDerived : IMiddleUnused
+ // {
+ // static abstract void N();
+ // }
+
+ // class A : IDerived {
+ // public static void M() {}
+ // public static void N() {}
+ // public static void O() {}
+ // }
+ //}
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/Dependencies/InstanceMethods.il b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/Dependencies/InstanceMethods.il
new file mode 100644
index 0000000000000..3679addefcbbd
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/Dependencies/InstanceMethods.il
@@ -0,0 +1,303 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+.assembly extern mscorlib { }
+
+.assembly 'library' { }
+
+.class public auto ansi abstract sealed beforefieldinit InstanceMethods
+ extends [mscorlib]System.Object
+{
+ // Nested Types
+ .class nested public auto ansi beforefieldinit TypeWithMethodAccessedViaInterface
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IInt
+ {
+ // Methods
+ .method private final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IInt::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodAccessedViaInterface::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodAccessedViaInterface::.ctor
+
+ } // end of class TypeWithMethodAccessedViaInterface
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodAccessedDirectly
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IInt
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IInt::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodAccessedDirectly::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodAccessedDirectly::.ctor
+
+ } // end of class TypeWithMethodAccessedDirectly
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodAccessedViaReflection
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IInt
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IInt::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodAccessedViaReflection::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodAccessedViaReflection::.ctor
+
+ } // end of class TypeWithMethodAccessedViaReflection
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodKeptByDynamicDependency
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IInt
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IInt::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodKeptByDynamicDependency::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodKeptByDynamicDependency::.ctor
+
+ } // end of class TypeWithMethodKeptByDynamicDependency
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodCalledDirectlyAndInterfaceUnreferenced
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IIntUnreferenced
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IIntUnreferenced::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodCalledDirectlyAndInterfaceUnreferenced::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodCalledDirectlyAndInterfaceUnreferenced::.ctor
+
+ } // end of class TypeWithMethodCalledDirectlyAndInterfaceUnreferenced
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced
+ extends [mscorlib]System.Object
+ implements InstanceMethods/IIntDerived
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetInt () cil managed
+ {
+ .override method instance int32 InstanceMethods/IIntBase::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced::GetInt
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced::.ctor
+
+ } // end of class TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced
+
+ .class nested public auto ansi beforefieldinit TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced
+ extends [mscorlib]System.Object
+ implements class InstanceMethods/IGeneric`1,
+ class InstanceMethods/IGeneric`1
+ {
+ // Methods
+ .method public final hidebysig newslot virtual
+ instance int32 GetIntInt () cil managed
+ {
+ .override method instance int32 class InstanceMethods/IGeneric`1::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced::GetIntInt
+
+ .method public final hidebysig newslot virtual
+ instance int32 GetIntFloat () cil managed
+ {
+ .override method instance int32 class InstanceMethods/IGeneric`1::GetInt()
+ // Method begins at RVA 0x2070
+ // Code size 2 (0x2)
+ .maxstack 8
+
+ IL_0000: ldc.i4.0
+ IL_0001: ret
+ } // end of method TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced::GetIntFloat
+
+ .method public hidebysig specialname rtspecialname
+ instance void .ctor () cil managed
+ {
+ // Method begins at RVA 0x2068
+ // Code size 7 (0x7)
+ .maxstack 8
+
+ IL_0000: ldarg.0
+ IL_0001: call instance void [mscorlib]System.Object::.ctor()
+ IL_0006: ret
+ } // end of method TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced::.ctor
+
+ } // end of class TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced
+
+ .class interface nested public auto ansi abstract beforefieldinit IInt
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance int32 GetInt () cil managed
+ {
+ } // end of method IInt::GetInt
+
+ } // end of class IInt
+
+ .class interface nested public auto ansi abstract beforefieldinit IIntUnreferenced
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance int32 GetInt () cil managed
+ {
+ } // end of method IIntUnreferenced::GetInt
+
+ } // end of class IIntUnreferenced
+
+ .class interface nested public auto ansi abstract beforefieldinit IIntBase
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance int32 GetInt () cil managed
+ {
+ } // end of method IIntBase::GetInt
+
+ } // end of class IIntBase
+
+ .class interface nested public auto ansi abstract beforefieldinit IIntDerived
+ implements InstanceMethods/IIntBase
+ {
+ } // end of class IIntDerived
+
+ .class interface nested public auto ansi abstract beforefieldinit IGeneric`1
+ {
+ // Methods
+ .method public hidebysig newslot abstract virtual
+ instance int32 GetInt () cil managed
+ {
+ } // end of method IGeneric`1::GetInt
+ } // end of class IGeneric`1
+
+ // Methods
+ .method public hidebysig static
+ void Test () cil managed
+ {
+ // Method begins at RVA 0x2050
+ // Code size 23 (0x17)
+ .maxstack 8
+
+ IL_0000: newobj instance void InstanceMethods/TypeWithMethodAccessedViaInterface::.ctor()
+ IL_0005: callvirt instance int32 InstanceMethods/IInt::GetInt()
+ IL_000a: pop
+ IL_000b: newobj instance void InstanceMethods/TypeWithMethodAccessedDirectly::.ctor()
+ IL_0010: call instance int32 InstanceMethods/TypeWithMethodAccessedDirectly::GetInt()
+ IL_0015: pop
+ IL_0016: ret
+ } // end of method InstanceMethods::Test
+} // end of class InstanceMethods
+
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/InstanceMethodsWithOverridesSwept.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/InstanceMethodsWithOverridesSwept.cs
new file mode 100644
index 0000000000000..40033f6c7e33c
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/InstanceMethodsWithOverridesSwept.cs
@@ -0,0 +1,80 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods
+{
+ ///
+ /// This test exercises the case where a public instance method has a .override directive pointing to an interface method when the interface method is not directly used for the type
+ /// Currently, the linker will always mark the .override method for instance methods, so there is not much testing required here.
+ /// However, if that were to change, this test should be updated to verify that the .override is removed if the .interfaceImpl is kept.
+ ///
+ [SetupLinkerArgument ("--skip-unresolved", "true")]
+ [Define ("IL_ASSEMBLY_AVAILABLE")]
+ [SetupCompileBefore ("library.dll", new string[] { "Dependencies/InstanceMethods.il" })]
+ [Kept]
+#if IL_ASSEMBLY_AVAILABLE
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaInterface))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedDirectly))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaReflection))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodKeptByDynamicDependency))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndInterfaceUnreferenced))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.IInt))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.IIntUnreferenced))] // Kept only because of the .override on the public implementation method
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.IIntBase))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.IIntDerived))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.IGeneric<>))]
+ [KeptTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced))]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaInterface), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedDirectly), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaReflection), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodKeptByDynamicDependency), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndInterfaceUnreferenced), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.IInt), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.IIntUnreferenced), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.IIntBase), ["GetInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced), ["GetIntInt()"])]
+ [KeptMemberInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced), ["GetIntFloat()"])] // Could be removed
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaInterface), "library.dll", typeof (InstanceMethods.IInt))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedDirectly), "library.dll", typeof (InstanceMethods.IInt))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodAccessedViaReflection), "library.dll", typeof (InstanceMethods.IInt))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodKeptByDynamicDependency), "library.dll", typeof (InstanceMethods.IInt))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndInterfaceUnreferenced), "library.dll", typeof (InstanceMethods.IIntUnreferenced))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced), "library.dll", typeof (InstanceMethods.IIntDerived))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", typeof (InstanceMethods.IIntDerived), "library.dll", typeof (InstanceMethods.IIntBase))]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "InstanceMethods/TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced", "library.dll", "InstanceMethods/IGeneric`1")]
+ [KeptInterfaceOnTypeInAssembly ("library.dll", "InstanceMethods/TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced", "library.dll", "InstanceMethods/IGeneric`1")]
+#endif
+ public class InstanceMethodsWithOverridesSwept
+ {
+ [Kept]
+ public static void Main ()
+ {
+#if IL_ASSEMBLY_AVAILABLE
+ InstanceMethods.Test ();
+ typeof (InstanceMethods.TypeWithMethodAccessedViaReflection).GetMethod ("GetInt").Invoke (null, null);
+ new InstanceMethods.TypeWithMethodCalledDirectlyAndInterfaceUnreferenced ().GetInt ();
+ new InstanceMethods.TypeWithMethodCalledDirectlyAndRecursiveInterfaceUnreferenced ().GetInt ();
+ new InstanceMethods.TypeWithMethodCalledDirectlyAndTwoGenericInterfacesUnreferenced ().GetIntInt ();
+#endif
+ KeepTypeThroughDynamicDependency ();
+ }
+
+#if IL_ASSEMBLY_AVAILABLE
+ [DynamicDependency ("GetInt()", typeof (InstanceMethods.TypeWithMethodKeptByDynamicDependency))]
+#endif
+ [Kept]
+ public static void KeepTypeThroughDynamicDependency ()
+ { }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs
new file mode 100644
index 0000000000000..01541dd73a17b
--- /dev/null
+++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/Inheritance.Interfaces/StaticInterfaceMethods/RemovedInterfaceImplementationRemovedOverride.cs
@@ -0,0 +1,278 @@
+// Copyright (c) .NET Foundation and contributors. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Mono.Linker.Tests.Cases.Expectations.Assertions;
+using Mono.Linker.Tests.Cases.Expectations.Metadata;
+
+namespace Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods
+{
+ [ExpectedNoWarnings]
+ public class RemovedInterfaceImplementationRemovedOverride
+ {
+ [Kept]
+ public static void Main ()
+ {
+ Basic.Test ();
+ GenericInterface.Test ();
+ GenericInterfaceGenericType.Test ();
+ PrivateExplicitImplementationReflectedOver.Test ();
+ InheritedInterfaces.Test ();
+ }
+
+ [Kept]
+ public class Basic
+ {
+ [Kept]
+ public static void Test ()
+ {
+ // Get message via generic method
+ var message = MessageFactory.GetMessage ();
+ Console.WriteLine (message);
+
+ // Get message directly from type
+ var message2 = TypeWithMethodAccessedDirectly.GetMessage ();
+ Console.WriteLine (message2);
+ }
+
+ [Kept]
+ public static class MessageFactory
+ {
+ [Kept]
+ public static string GetMessage () where T : IStaticMethod
+ {
+ return T.GetMessage ();
+ }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IStaticMethod))]
+ public class TypeWithMethodAccessedViaInterface : IStaticMethod
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [KeptOverride (typeof (IStaticMethod))]
+ public static string GetMessage () => $"Hello from {nameof (TypeWithMethodAccessedViaInterface)}, the time is {TimeOnly.FromDateTime (DateTime.Now)}";
+ }
+
+ [Kept]
+ public class TypeWithMethodAccessedDirectly : IStaticMethod
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [RemovedOverride (typeof (IStaticMethod))]
+ public static string GetMessage () => $"Hello from {nameof (TypeWithMethodAccessedDirectly)}, the time is {TimeOnly.FromDateTime (DateTime.Now)}";
+ }
+
+ [Kept]
+ public interface IStaticMethod
+ {
+ [Kept]
+ abstract static string GetMessage ();
+ }
+ }
+
+ [Kept]
+ public class GenericInterface
+ {
+ [Kept]
+ public static void Test ()
+ {
+ // Get message via generic method
+ var message = MessageFactory.GetMessage ();
+ Console.WriteLine (message);
+
+ // Get message directly from type
+ var message2 = TypeWithMethodAccessedDirectly.GetMessage ();
+ Console.WriteLine (message2);
+ }
+
+ [Kept]
+ public static class MessageFactory
+ {
+ [Kept]
+ public static U GetMessage () where T : IStaticAbstractMethodGeneric
+ {
+ return T.GetMessage ();
+ }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IStaticAbstractMethodGeneric))]
+ public class TypeWithMethodAccessedViaInterface : IStaticAbstractMethodGeneric
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [KeptOverride (typeof (IStaticAbstractMethodGeneric))]
+ public static int GetMessage () => 0;
+ }
+
+ [Kept]
+ public class TypeWithMethodAccessedDirectly : IStaticAbstractMethodGeneric
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [RemovedOverride (typeof (IStaticAbstractMethodGeneric))]
+ public static int GetMessage () => 0;
+ }
+
+ [Kept]
+ public interface IStaticAbstractMethodGeneric
+ {
+ [Kept]
+ abstract static T GetMessage ();
+ }
+ }
+
+ [Kept]
+ public class GenericInterfaceGenericType
+ {
+ [Kept]
+ public static void Test ()
+ {
+ // Get message via generic method
+ var message = MessageFactory.GetMessage, int> ();
+ Console.WriteLine (message);
+
+ // Get message directly from type
+ var message2 = TypeWithMethodAccessedDirectly.GetMessage ();
+ Console.WriteLine (message2);
+ }
+
+ [Kept]
+ public static class MessageFactory
+ {
+ [Kept]
+ public static U GetMessage () where T : IStaticAbstractMethodGeneric
+ {
+ return T.GetMessage ();
+ }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IStaticAbstractMethodGeneric<>), "T")]
+ public class TypeWithMethodAccessedViaInterface : IStaticAbstractMethodGeneric
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [KeptOverride ("Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods.RemovedInterfaceImplementationRemovedOverride/" +
+ "GenericInterfaceGenericType/IStaticAbstractMethodGeneric`1")]
+ public static T GetMessage () => default;
+ }
+
+ [Kept]
+ public class TypeWithMethodAccessedDirectly : IStaticAbstractMethodGeneric
+ {
+ // Force the string to have dynamic element so that it doesn't get compiled in as a literal
+ [Kept]
+ [RemovedOverride ("Mono.Linker.Tests.Cases.Inheritance.Interfaces.StaticInterfaceMethods.RemovedInterfaceImplementationRemovedOverride/" +
+ "GenericInterfaceGenericType/IStaticAbstractMethodGeneric`1")]
+ public static T GetMessage () => default;
+ }
+
+ [Kept]
+ public interface IStaticAbstractMethodGeneric
+ {
+ [Kept]
+ abstract static T GetMessage ();
+ }
+ }
+
+ [Kept]
+ public class PrivateExplicitImplementationReflectedOver
+ {
+ [Kept]
+ public static void Test ()
+ {
+ IInstanceMethod i = new MyType ();
+ Console.WriteLine (i.GetMessage ());
+ var type = typeof (MyTypeReflected);
+ var method = type.GetMethod ("GetMessage");
+ method.Invoke (null, null);
+ }
+
+ [Kept]
+ public interface IInstanceMethod
+ {
+ [Kept]
+ public string GetMessage ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IInstanceMethod))]
+ public class MyType : IInstanceMethod
+ {
+ [Kept]
+ public MyType () { }
+
+ [Kept]
+ [KeptOverride (typeof (IInstanceMethod))]
+ string IInstanceMethod.GetMessage () => "hello";
+ }
+
+ [Kept]
+ // Reflecting over MyTypeReflected to get the GetMessage method makes MyTypeReflected 'RelevantToVariantCasting' which marks the interface
+ // It's hard to think of a scenario where the method would be kept but not the interface implementation
+ [KeptInterface (typeof (IInstanceMethod))]
+ public class MyTypeReflected : IInstanceMethod
+ {
+ public MyTypeReflected () { }
+
+ [Kept]
+ [KeptOverride (typeof (IInstanceMethod))]
+ [ExpectBodyModified]
+ string IInstanceMethod.GetMessage () => "hello";
+ }
+ }
+
+ [Kept]
+ public static class InheritedInterfaces
+ {
+ [Kept]
+ public static void Test ()
+ {
+ UseInterface ();
+ UsedDirectly.M ();
+ }
+
+ [Kept]
+ static void UseInterface () where T : IDerived
+ {
+ T.M ();
+ }
+
+ [Kept]
+ interface IBase
+ {
+ [Kept]
+ static abstract void M ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IBase))]
+ interface IDerived : IBase { }
+
+ [Kept]
+ [KeptInterface (typeof (IDerived))]
+ [KeptInterface (typeof (IBase))]
+ class UsedThroughInterface : IDerived
+ {
+ [Kept]
+ [KeptOverride (typeof (IBase))]
+ public static void M () { }
+ }
+
+ [Kept]
+ class UsedDirectly : IDerived
+ {
+ [Kept]
+ [RemovedOverride (typeof (IBase))]
+ public static void M () { }
+ }
+ }
+ }
+}
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
index 467c2272c1bed..6e45fbadcdb52 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
@@ -5,12 +5,9 @@
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
-using System.Linq.Expressions;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
-using Mono.Linker.Dataflow;
-using Mono.Linker.Tests.Cases.CppCLI;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Extensions;
using NUnit.Framework;
@@ -345,54 +342,38 @@ IEnumerable VerifyInterfaces (TypeDefinition src, TypeDefinition linked)
}
if (expectedInterfaces.Any ()) {
- yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join(Environment.NewLine, expectedInterfaces)}\n";
+ yield return $"Expected interfaces were not found on {src}. Expected to find: \n{string.Join (Environment.NewLine, expectedInterfaces)}\n";
}
}
}
- void VerifyOverrides (MethodDefinition original, MethodDefinition linked)
+ IEnumerable VerifyOverrides (MethodDefinition original, MethodDefinition linked)
{
if (linked is null)
- return;
+ yield break;
var expectedBaseTypesOverridden = new HashSet (original.CustomAttributes
.Where (ca => ca.AttributeType.Name == nameof (KeptOverrideAttribute))
- .Select (ca => (ca.ConstructorArguments[0].Value as TypeReference).FullName));
+ .Select (ca => (ca.ConstructorArguments[0].Value as TypeReference)?.FullName ?? (string) ca.ConstructorArguments[0].Value));
var originalBaseTypesOverridden = new HashSet (original.Overrides.Select (ov => ov.DeclaringType.FullName));
var linkedBaseTypesOverridden = new HashSet (linked.Overrides.Select (ov => ov.DeclaringType.FullName));
foreach (var expectedBaseType in expectedBaseTypesOverridden) {
- Assert.IsTrue (originalBaseTypesOverridden.Contains (expectedBaseType),
- $"Method {linked.FullName} was expected to keep override {expectedBaseType}::{linked.Name}, " +
- "but it wasn't in the unlinked assembly");
- Assert.IsTrue (linkedBaseTypesOverridden.Contains (expectedBaseType),
- $"Method {linked.FullName} was expected to override {expectedBaseType}::{linked.Name}");
+ if (!originalBaseTypesOverridden.Contains (expectedBaseType)) {
+ yield return $"Method {linked.FullName} was expected to keep override {expectedBaseType}::{linked.Name}, " +
+ "but it wasn't in the unlinked assembly" + string.Join (Environment.NewLine, originalBaseTypesOverridden);
+ } else if (!linkedBaseTypesOverridden.Contains (expectedBaseType)) {
+ yield return $"Method {linked.FullName} was expected to override {expectedBaseType}::{linked.Name}";
+ }
}
var expectedBaseTypesNotOverridden = new HashSet (original.CustomAttributes
.Where (ca => ca.AttributeType.Name == nameof (RemovedOverrideAttribute))
- .Select (ca => (ca.ConstructorArguments[0].Value as TypeReference).FullName));
+ .Select (ca => (ca.ConstructorArguments[0].Value as TypeReference)?.FullName ?? (string) ca.ConstructorArguments[0].Value));
foreach (var expectedRemovedBaseType in expectedBaseTypesNotOverridden) {
- Assert.IsTrue (originalBaseTypesOverridden.Contains (expectedRemovedBaseType),
- $"Method {linked.FullName} was expected to remove override {expectedRemovedBaseType}::{linked.Name}, " +
- $"but it wasn't in the unlinked assembly");
- Assert.IsFalse (linkedBaseTypesOverridden.Contains (expectedRemovedBaseType),
- $"Method {linked.FullName} was expected to not override {expectedRemovedBaseType}::{linked.Name}");
- }
-
- foreach (var overriddenMethod in linked.Overrides) {
- if (overriddenMethod.Resolve () is not MethodDefinition overriddenDefinition) {
- Assert.Fail ($"Method {linked.GetDisplayName ()} overrides method {overriddenMethod} which does not exist");
- } else if (overriddenDefinition.DeclaringType.IsInterface) {
- Assert.True (linked.DeclaringType.Interfaces.Select (i => i.InterfaceType).Contains (overriddenMethod.DeclaringType),
- $"Method {linked} overrides method {overriddenMethod}, but {linked.DeclaringType} does not implement interface {overriddenMethod.DeclaringType}");
- } else {
- TypeDefinition baseType = linked.DeclaringType;
- TypeReference overriddenType = overriddenMethod.DeclaringType;
- while (baseType is not null) {
- if (baseType.Equals (overriddenType))
- break;
- if (baseType.Resolve ()?.BaseType is null)
- Assert.Fail ($"Method {linked} overrides method {overriddenMethod} from, but {linked.DeclaringType} does not inherit from type {overriddenMethod.DeclaringType}");
- }
+ if (!originalBaseTypesOverridden.Contains (expectedRemovedBaseType)) {
+ yield return $"Method {linked.FullName} was expected to remove override {expectedRemovedBaseType}::{linked.Name}, " +
+ $"but it wasn't in the unlinked assembly";
+ } else if (linkedBaseTypesOverridden.Contains (expectedRemovedBaseType)) {
+ yield return $"Method {linked.FullName} was expected to not override {expectedRemovedBaseType}::{linked.Name}";
}
}
}
@@ -551,6 +532,9 @@ IEnumerable VerifyMethodInternal (MethodDefinition src, MethodDefinition
yield return $"Method `{src.FullName}' should have been removed";
}
+ foreach (var err in VerifyOverrides (src, linked))
+ yield return err;
+
foreach (var err in VerifyMethodKept (src, linked, compilerGenerated))
yield return err;
}
@@ -935,8 +919,7 @@ IEnumerable VerifyPrivateImplementationDetails (TypeDefinition original,
TypeDefinition srcImplementationDetails;
TypeDefinition linkedImplementationDetails;
- if (VerifyPrivateImplementationDetailsType (original.Module, linked.Module, out srcImplementationDetails, out linkedImplementationDetails, out var errors))
- {
+ if (VerifyPrivateImplementationDetailsType (original.Module, linked.Module, out srcImplementationDetails, out linkedImplementationDetails, out var errors)) {
foreach (var err in errors)
yield return err;
}
@@ -958,13 +941,13 @@ static bool VerifyPrivateImplementationDetailsType (ModuleDefinition src, Module
errs = [];
srcImplementationDetails = src.Types.FirstOrDefault (t => IsPrivateImplementationDetailsType (t));
if (srcImplementationDetails == null)
- errs.Append("Could not locate in the original assembly. Does your test use initializers?");
+ errs.Append ("Could not locate in the original assembly. Does your test use initializers?");
linkedImplementationDetails = linked.Types.FirstOrDefault (t => IsPrivateImplementationDetailsType (t));
if (linkedImplementationDetails == null)
- errs.Append("Could not locate in the linked assembly");
+ errs.Append ("Could not locate in the linked assembly");
- return !errs.Any();
+ return !errs.Any ();
}
protected virtual IEnumerable VerifyArrayInitializers (MethodDefinition src, MethodDefinition linked)
@@ -982,9 +965,8 @@ protected virtual IEnumerable VerifyArrayInitializers (MethodDefinition
yield return $"`{nameof (KeptInitializerData)}` cannot be used on methods that don't have bodies";
TypeDefinition srcImplementationDetails;
TypeDefinition linkedImplementationDetails;
- if (VerifyPrivateImplementationDetailsType (src.Module, linked.Module, out srcImplementationDetails, out linkedImplementationDetails, out var errs))
- {
- foreach(var err in errs)
+ if (VerifyPrivateImplementationDetailsType (src.Module, linked.Module, out srcImplementationDetails, out linkedImplementationDetails, out var errs)) {
+ foreach (var err in errs)
yield return err;
}
diff --git a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
index aff0d6ba1e6b6..e9e9b94de191e 100644
--- a/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
+++ b/src/tools/illink/test/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs
@@ -10,16 +10,13 @@
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
-using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Linker.Tests.Cases.Expectations.Assertions;
using Mono.Linker.Tests.Cases.Expectations.Metadata;
using Mono.Linker.Tests.Extensions;
-using Mono.Linker.Tests.TestCases;
using Mono.Linker.Tests.TestCasesRunner.ILVerification;
using NUnit.Framework;
-using WellKnownType = ILLink.Shared.TypeSystemProxy.WellKnownType;
namespace Mono.Linker.Tests.TestCasesRunner
{
@@ -69,13 +66,12 @@ protected static void ValidateTypeRefsHaveValidAssemblyRefs (AssemblyDefinition
throw new NotImplementedException ($"Unexpected scope type '{exportedType.Scope.GetType ()}' for exported type '{exportedType.FullName}'");
}
continue;
- case AssemblyNameReference:
- {
- // There should be an AssemblyRef row for this assembly
- var assemblyRef = linked.MainModule.AssemblyReferences.Single (ar => ar.Name == typeRef.Scope.Name);
- Assert.IsNotNull (assemblyRef, $"Type reference '{typeRef.FullName}' has a reference to assembly '{typeRef.Scope.Name}' which is not a reference of '{linked.FullName}'");
- continue;
- }
+ case AssemblyNameReference: {
+ // There should be an AssemblyRef row for this assembly
+ var assemblyRef = linked.MainModule.AssemblyReferences.Single (ar => ar.Name == typeRef.Scope.Name);
+ Assert.IsNotNull (assemblyRef, $"Type reference '{typeRef.FullName}' has a reference to assembly '{typeRef.Scope.Name}' which is not a reference of '{linked.FullName}'");
+ continue;
+ }
default:
throw new NotImplementedException ($"Unexpected scope type '{typeRef.Scope.GetType ()}' for type reference '{typeRef.FullName}'");
}
@@ -101,10 +97,10 @@ public virtual void Check (TrimmedTestCaseResult linkResult)
PerformOutputAssemblyChecks (original, linkResult.OutputAssemblyPath.Parent);
PerformOutputSymbolChecks (original, linkResult.OutputAssemblyPath.Parent);
- if (!HasActiveSkipKeptItemsValidationAttribute(linkResult.TestCase.FindTypeDefinition (original))) {
+ if (!HasActiveSkipKeptItemsValidationAttribute (linkResult.TestCase.FindTypeDefinition (original))) {
CreateAssemblyChecker (original, linked, linkResult).Verify ();
}
- CreateILChecker ().Check(linkResult, original);
+ CreateILChecker ().Check (linkResult, original);
VerifyExpectedDependencyTrace (linkResult.MetadataProvider, linkResult.OutputAssemblyPath);
}
@@ -223,12 +219,12 @@ void PerformOutputSymbolChecks (AssemblyDefinition original, NPath outputDirecto
void VerifyExitCode (TrimmedTestCaseResult linkResult, AssemblyDefinition original)
{
- if (TryGetCustomAttribute (original, nameof(ExpectNonZeroExitCodeAttribute), out var attr)) {
+ if (TryGetCustomAttribute (original, nameof (ExpectNonZeroExitCodeAttribute), out var attr)) {
var expectedExitCode = (int) attr.ConstructorArguments[0].Value;
- Assert.AreEqual (expectedExitCode, linkResult.ExitCode, $"Expected exit code {expectedExitCode} but got {linkResult.ExitCode}. Output was:\n{FormatLinkerOutput()}");
+ Assert.AreEqual (expectedExitCode, linkResult.ExitCode, $"Expected exit code {expectedExitCode} but got {linkResult.ExitCode}. Output was:\n{FormatLinkerOutput ()}");
} else {
if (linkResult.ExitCode != 0) {
- Assert.Fail ($"Linker exited with an unexpected non-zero exit code of {linkResult.ExitCode} and output:\n{FormatLinkerOutput()}");
+ Assert.Fail ($"Linker exited with an unexpected non-zero exit code of {linkResult.ExitCode} and output:\n{FormatLinkerOutput ()}");
}
}
@@ -390,6 +386,12 @@ void VerifyLinkingOfOtherAssemblies (AssemblyDefinition original)
Assert.Fail ($"Type `{expectedTypeName}` should have been kept in assembly {assemblyName}");
VerifyExpectedInstructionSequenceOnMemberInAssembly (checkAttrInAssembly, linkedType);
break;
+ case nameof (RemovedOverrideOnMethodInAssemblyAttribute):
+ VerifyRemovedOverrideOnMethodInAssembly (checkAttrInAssembly, linkedType);
+ break;
+ case nameof (KeptOverrideOnMethodInAssemblyAttribute):
+ VerifyKeptOverrideOnMethodInAssembly (checkAttrInAssembly, linkedType);
+ break;
default:
UnhandledOtherAssemblyAssertion (expectedTypeName, checkAttrInAssembly, linkedType);
break;
@@ -532,18 +534,18 @@ void VerifyKeptInterfaceOnTypeInAssembly (CustomAttribute inAssemblyAttribute, T
var interfaceAssemblyName = inAssemblyAttribute.ConstructorArguments[2].Value.ToString ();
var interfaceType = inAssemblyAttribute.ConstructorArguments[3].Value;
+ string originalInterfaceName = interfaceType as string ?? GetOriginalTypeFromInAssemblyAttribute (interfaceAssemblyName, interfaceType).FullName;
- var originalInterface = GetOriginalTypeFromInAssemblyAttribute (interfaceAssemblyName, interfaceType);
if (!originalType.HasInterfaces)
Assert.Fail ("Invalid assertion. Original type does not have any interfaces");
- var originalInterfaceImpl = GetMatchingInterfaceImplementationOnType (originalType, originalInterface.FullName);
+ var originalInterfaceImpl = GetMatchingInterfaceImplementationOnType (originalType, originalInterfaceName);
if (originalInterfaceImpl == null)
- Assert.Fail ($"Invalid assertion. Original type never had an interface of type `{originalInterface}`");
+ Assert.Fail ($"Invalid assertion. Original type never had an interface of type `{interfaceType}`");
- var linkedInterfaceImpl = GetMatchingInterfaceImplementationOnType (linkedType, originalInterface.FullName);
+ var linkedInterfaceImpl = GetMatchingInterfaceImplementationOnType (linkedType, originalInterfaceName);
if (linkedInterfaceImpl == null)
- Assert.Fail ($"Expected `{linkedType}` to have interface of type {originalInterface.FullName}");
+ Assert.Fail ($"Expected `{linkedType}` to have interface of type {originalInterfaceName}");
}
void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDefinition linkedType)
@@ -564,12 +566,7 @@ void VerifyKeptBaseOnTypeInAssembly (CustomAttribute inAssemblyAttribute, TypeDe
protected static InterfaceImplementation GetMatchingInterfaceImplementationOnType (TypeDefinition type, string expectedInterfaceTypeName)
{
return type.Interfaces.FirstOrDefault (impl => {
- var resolvedImpl = impl.InterfaceType.Resolve ();
-
- if (resolvedImpl == null)
- Assert.Fail ($"Failed to resolve interface : `{impl.InterfaceType}` on `{type}`");
-
- return resolvedImpl.FullName == expectedInterfaceTypeName;
+ return impl.InterfaceType.FullName == expectedInterfaceTypeName;
});
}
@@ -636,6 +633,26 @@ void VerifyKeptMemberInAssembly (CustomAttribute inAssemblyAttribute, TypeDefini
}
}
+ void VerifyRemovedOverrideOnMethodInAssembly (CustomAttribute attr, TypeDefinition type)
+ {
+ var methodname = (string) attr.ConstructorArguments[2].Value;
+ var method = type.Methods.FirstOrDefault (m => m.Name == methodname);
+ var overriddenMethodName = (string) attr.ConstructorArguments[3].Value;
+ if (method.Overrides.Any (m => m.FullName == overriddenMethodName)) {
+ Assert.Fail ($"Expected method {method.FullName} to not have .override for {overriddenMethodName}");
+ }
+ }
+
+ void VerifyKeptOverrideOnMethodInAssembly (CustomAttribute attr, TypeDefinition type)
+ {
+ var methodname = (string) attr.ConstructorArguments[2].Value;
+ var method = type.Methods.FirstOrDefault (m => m.Name == methodname);
+ var overriddenMethodName = (string) attr.ConstructorArguments[3].Value;
+ if (!method.Overrides.Any (m => m.FullName == overriddenMethodName)) {
+ Assert.Fail ($"Expected method {method.FullName} to have .override for {overriddenMethodName}");
+ }
+ }
+
protected virtual bool TryVerifyKeptMemberInAssemblyAsField (string memberName, TypeDefinition originalType, TypeDefinition linkedType)
{
var originalFieldMember = originalType.Fields.FirstOrDefault (m => m.Name == memberName);
@@ -740,7 +757,7 @@ void VerifyKeptAllTypesAndMembersInAssembly (AssemblyDefinition linked)
static bool IsProducedByLinker (CustomAttribute attr)
{
if (attr.Constructor.Parameters.Count > 2 && attr.ConstructorArguments[^2].Type.Name == "Tool") {
- return ((Tool)attr.ConstructorArguments[^2].Value).HasFlag (Tool.Trimmer) == true;
+ return ((Tool) attr.ConstructorArguments[^2].Value).HasFlag (Tool.Trimmer) == true;
}
var producedBy = attr.GetPropertyValue ("ProducedBy");
return producedBy is null ? true : ((Tool) producedBy).HasFlag (Tool.Trimmer);
@@ -764,7 +781,7 @@ static IEnumerable GetAttributeProviders (AssemblyDefi
void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logger, bool checkRemainingErrors)
{
ImmutableArray allMessages = logger.GetLoggedMessages ();
- List unmatchedMessages = [..allMessages];
+ List unmatchedMessages = [.. allMessages];
List<(ICustomAttributeProvider, CustomAttribute)> expectedNoWarningsAttributes = new ();
List missingMessageWarnings = [];
List unexpectedMessageWarnings = [];
@@ -805,28 +822,27 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge
}
break;
- case nameof (ExpectedWarningAttribute) or nameof(UnexpectedWarningAttribute): {
+ case nameof (ExpectedWarningAttribute) or nameof (UnexpectedWarningAttribute): {
var expectedWarningCode = (string) attr.GetConstructorArgumentValue (0);
if (!expectedWarningCode.StartsWith ("IL")) {
Assert.Fail ($"The warning code specified in {attr.AttributeType.Name} must start with the 'IL' prefix. Specified value: '{expectedWarningCode}'.");
}
- IEnumerable expectedMessageContains = attr.Constructor.Parameters switch
- {
- // ExpectedWarningAttribute(string warningCode, params string[] expectedMessages)
- // ExpectedWarningAttribute(string warningCode, string[] expectedMessages, Tool producedBy, string issueLink)
- [_, { ParameterType.IsArray: true }, ..]
- => ((CustomAttributeArgument[])attr.ConstructorArguments[1].Value)
- .Select(caa => (string)caa.Value),
- // ExpectedWarningAttribute(string warningCode, string expectedMessage1, string expectedMessage2, Tool producedBy, string issueLink)
- [_, { ParameterType.Name: "String" }, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
- => [(string)attr.GetConstructorArgumentValue(1), (string)attr.GetConstructorArgumentValue(2)],
- // ExpectedWarningAttribute(string warningCode, string expectedMessage, Tool producedBy, string issueLink)
- [_, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
- => [(string)attr.GetConstructorArgumentValue(1)],
- // ExpectedWarningAttribute(string warningCode, Tool producedBy, string issueLink)
- [_, { ParameterType.Name: "Tool" }, _]
- => [],
- _ => throw new UnreachableException(),
+ IEnumerable expectedMessageContains = attr.Constructor.Parameters switch {
+ // ExpectedWarningAttribute(string warningCode, params string[] expectedMessages)
+ // ExpectedWarningAttribute(string warningCode, string[] expectedMessages, Tool producedBy, string issueLink)
+ [_, { ParameterType.IsArray: true }, ..]
+ => ((CustomAttributeArgument[]) attr.ConstructorArguments[1].Value)
+ .Select (caa => (string) caa.Value),
+ // ExpectedWarningAttribute(string warningCode, string expectedMessage1, string expectedMessage2, Tool producedBy, string issueLink)
+ [_, { ParameterType.Name: "String" }, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
+ => [(string) attr.GetConstructorArgumentValue (1), (string) attr.GetConstructorArgumentValue (2)],
+ // ExpectedWarningAttribute(string warningCode, string expectedMessage, Tool producedBy, string issueLink)
+ [_, { ParameterType.Name: "String" }, { ParameterType.Name: "Tool" }, _]
+ => [(string) attr.GetConstructorArgumentValue (1)],
+ // ExpectedWarningAttribute(string warningCode, Tool producedBy, string issueLink)
+ [_, { ParameterType.Name: "Tool" }, _]
+ => [],
+ _ => throw new UnreachableException (),
};
string fileName = (string) attr.GetPropertyValue ("FileName");
int? sourceLine = (int?) attr.GetPropertyValue ("SourceLine");
@@ -991,14 +1007,12 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge
break;
}
- if (unexpectedWarningMessage is not null)
- {
- unexpectedMessageWarnings.Add($"Unexpected warning found: {unexpectedWarningMessage}");
+ if (unexpectedWarningMessage is not null) {
+ unexpectedMessageWarnings.Add ($"Unexpected warning found: {unexpectedWarningMessage}");
}
}
- if (missingMessageWarnings.Any ())
- {
+ if (missingMessageWarnings.Any ()) {
missingMessageWarnings.Add ("Unmatched Messages:" + Environment.NewLine);
missingMessageWarnings.AddRange (unmatchedMessages.Select (m => m.ToString ()));
missingMessageWarnings.Add (Environment.NewLine + "All Messages:" + Environment.NewLine);
@@ -1006,8 +1020,7 @@ void VerifyLoggedMessages (AssemblyDefinition original, TrimmingTestLogger logge
Assert.Fail (string.Join (Environment.NewLine, missingMessageWarnings));
}
- if (unexpectedMessageWarnings.Any())
- {
+ if (unexpectedMessageWarnings.Any ()) {
Assert.Fail (string.Join (Environment.NewLine, unexpectedMessageWarnings));
}