Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/linker.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs37
-rw-r--r--src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs95
-rw-r--r--src/linker/Linker.Dataflow/ReflectionPatternContext.cs2
-rw-r--r--src/linker/Linker.Steps/MarkStep.cs15
-rw-r--r--test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs2
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs142
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs118
-rw-r--r--test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs249
-rw-r--r--test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs2
9 files changed, 629 insertions, 33 deletions
diff --git a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs
index f71e7c9d2..477174caa 100644
--- a/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs
+++ b/src/linker/Linker.Dataflow/DynamicallyAccessedMembersBinder.cs
@@ -10,12 +10,19 @@ using Mono.Cecil;
namespace Mono.Linker
{
+ // Temporary workaround - should be removed once linker can be upgraded to build against
+ // high enough version of the framework which has this enum value.
+ internal static class DynamicallyAccessedMemberTypesOverlay
+ {
+ public const DynamicallyAccessedMemberTypes Interfaces = (DynamicallyAccessedMemberTypes) 0x2000;
+ }
+
internal static class DynamicallyAccessedMembersBinder
{
- // Returns the members of the type bound by memberTypes. For MemberTypes.All, this returns a single null result.
- // This sentinel value allows callers to handle the case where MemberTypes.All conceptually binds to the entire type
- // including all recursive nested members.
- public static IEnumerable<IMemberDefinition> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes)
+ // Returns the members of the type bound by memberTypes. For DynamicallyAccessedMemberTypes.All, this returns a single null result.
+ // This sentinel value allows callers to handle the case where DynamicallyAccessedMemberTypes.All conceptually binds to the entire type
+ // including all recursive nested members.
+ public static IEnumerable<IMetadataTokenProvider> GetDynamicallyAccessedMembers (this TypeDefinition typeDefinition, LinkContext context, DynamicallyAccessedMemberTypes memberTypes)
{
if (memberTypes == DynamicallyAccessedMemberTypes.All) {
yield return null;
@@ -86,6 +93,11 @@ namespace Mono.Linker
foreach (var e in typeDefinition.GetEventsOnTypeHierarchy (context, filter: null, bindingFlags: BindingFlags.Public))
yield return e;
}
+
+ if (memberTypes.HasFlag (DynamicallyAccessedMemberTypesOverlay.Interfaces)) {
+ foreach (var i in typeDefinition.GetAllInterfaceImplementations (context))
+ yield return i;
+ }
}
public static IEnumerable<MethodDefinition> GetConstructorsOnType (this TypeDefinition type, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null)
@@ -298,5 +310,22 @@ namespace Mono.Linker
onBaseType = true;
}
}
+
+ public static IEnumerable<InterfaceImplementation> GetAllInterfaceImplementations (this TypeDefinition type, LinkContext context)
+ {
+ while (type != null) {
+ foreach (var i in type.Interfaces) {
+ yield return i;
+
+ TypeDefinition interfaceType = context.TryResolveTypeDefinition (i.InterfaceType);
+ if (interfaceType != null) {
+ foreach (var innerInterface in interfaceType.GetAllInterfaceImplementations (context))
+ yield return innerInterface;
+ }
+ }
+
+ type = context.TryResolveTypeDefinition (type.BaseType);
+ }
+ }
}
} \ No newline at end of file
diff --git a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
index ff63e562b..6328b4a30 100644
--- a/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
+++ b/src/linker/Linker.Dataflow/ReflectionMethodBodyScanner.cs
@@ -18,6 +18,17 @@ namespace Mono.Linker.Dataflow
class ReflectionMethodBodyScanner : MethodBodyScanner
{
readonly MarkStep _markStep;
+ static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes ();
+
+ static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes ()
+ {
+ HashSet<DynamicallyAccessedMemberTypes> values = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes))
+ .Cast<DynamicallyAccessedMemberTypes> ()
+ .ToHashSet ();
+ if (!values.Contains (DynamicallyAccessedMemberTypesOverlay.Interfaces))
+ values.Add (DynamicallyAccessedMemberTypesOverlay.Interfaces);
+ return values.ToArray ();
+ }
public static bool RequiresReflectionMethodBodyScannerForCallSite (LinkContext context, MethodReference calledMethod)
{
@@ -263,6 +274,7 @@ namespace Mono.Linker.Dataflow
Type_GetNestedTypes,
Type_GetMember,
Type_GetMembers,
+ Type_GetInterface,
Type_get_AssemblyQualifiedName,
Type_get_UnderlyingSystemType,
Type_get_BaseType,
@@ -469,6 +481,15 @@ namespace Mono.Linker.Dataflow
&& calledMethod.HasThis
=> IntrinsicId.Type_GetMembers,
+ // System.Type.GetInterface (string)
+ // System.Type.GetInterface (string, bool)
+ "GetInterface" when calledMethod.IsDeclaredOnType ("System", "Type")
+ && calledMethod.HasParameterOfType (0, "System", "String")
+ && calledMethod.HasThis
+ && (calledMethod.Parameters.Count == 1 ||
+ (calledMethod.Parameters.Count == 2 && calledMethod.HasParameterOfType (1, "System", "Boolean")))
+ => IntrinsicId.Type_GetInterface,
+
// System.Type.AssemblyQualifiedName
"get_AssemblyQualifiedName" when calledMethod.IsDeclaredOnType ("System", "Type")
&& !calledMethod.HasParameters
@@ -1469,6 +1490,33 @@ namespace Mono.Linker.Dataflow
break;
//
+ // GetInterface (String)
+ // GetInterface (String, bool)
+ //
+ case IntrinsicId.Type_GetInterface: {
+ reflectionContext.AnalyzingPattern ();
+
+ foreach (var value in methodParams[0].UniqueValues ()) {
+ // For now no support for marking a single interface by name. We would have to correctly support
+ // mangled names for generics to do that correctly. Simply mark all interfaces on the type for now.
+
+ // Require Interfaces annotation
+ RequireDynamicallyAccessedMembers (ref reflectionContext, DynamicallyAccessedMemberTypesOverlay.Interfaces, value, calledMethodDefinition);
+
+ // Interfaces is transitive, so the return values will always have at least Interfaces annotation
+ DynamicallyAccessedMemberTypes returnMemberTypes = DynamicallyAccessedMemberTypesOverlay.Interfaces;
+
+ // Propagate All annotation across the call - All is a superset of Interfaces
+ if (value is LeafValueWithDynamicallyAccessedMemberNode annotatedNode
+ && annotatedNode.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.All)
+ returnMemberTypes = DynamicallyAccessedMemberTypes.All;
+
+ methodReturnValue = MergePointValue.MergeValues (methodReturnValue, CreateMethodReturnValue (calledMethod, returnMemberTypes));
+ }
+ }
+ break;
+
+ //
// System.Activator
//
// static CreateInstance (System.Type type)
@@ -1738,7 +1786,7 @@ namespace Mono.Linker.Dataflow
return true;
}
- private bool AnalyzeGenericInstatiationTypeArray (ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList<GenericParameter> genericParameters)
+ bool AnalyzeGenericInstatiationTypeArray (ValueNode arrayParam, ref ReflectionPatternContext reflectionContext, MethodReference calledMethod, IList<GenericParameter> genericParameters)
{
bool hasRequirements = false;
foreach (var genericParameter in genericParameters) {
@@ -1868,19 +1916,24 @@ namespace Mono.Linker.Dataflow
reflectionContext.RecordHandledPattern ();
} else if (uniqueValue is LeafValueWithDynamicallyAccessedMemberNode valueWithDynamicallyAccessedMember) {
if (!valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes.HasFlag (requiredMemberTypes)) {
- string missingMemberTypes = $"'{nameof (DynamicallyAccessedMemberTypes.All)}'";
+ string missingMemberTypes = $"'{nameof (DynamicallyAccessedMemberTypes)}.{nameof (DynamicallyAccessedMemberTypes.All)}'";
if (requiredMemberTypes != DynamicallyAccessedMemberTypes.All) {
- var missingMemberTypesList = Enum.GetValues (typeof (DynamicallyAccessedMemberTypes))
- .Cast<DynamicallyAccessedMemberTypes> ()
+ var missingMemberTypesList = AllDynamicallyAccessedMemberTypes
.Where (damt => (requiredMemberTypes & ~valueWithDynamicallyAccessedMember.DynamicallyAccessedMemberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None)
- .Select (damt => damt.ToString ()).ToList ();
+ .ToList ();
- if (missingMemberTypesList.Contains (nameof (DynamicallyAccessedMemberTypes.PublicConstructors)) &&
- missingMemberTypesList.SingleOrDefault (x => x == nameof (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)) is var ppc &&
- ppc != null)
+ if (missingMemberTypesList.Contains (DynamicallyAccessedMemberTypes.PublicConstructors) &&
+ missingMemberTypesList.SingleOrDefault (x => x == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor) is var ppc &&
+ ppc != DynamicallyAccessedMemberTypes.None)
missingMemberTypesList.Remove (ppc);
- missingMemberTypes = string.Join (", ", missingMemberTypesList.Select (mmt => $"'DynamicallyAccessedMemberTypes.{mmt}'"));
+ missingMemberTypes = string.Join (", ", missingMemberTypesList.Select (mmt => {
+ string mmtName = mmt == DynamicallyAccessedMemberTypesOverlay.Interfaces
+ ? nameof (DynamicallyAccessedMemberTypesOverlay.Interfaces)
+ : mmt.ToString ();
+
+ return $"'{nameof (DynamicallyAccessedMemberTypes)}.{mmtName}'";
+ }));
}
switch ((valueWithDynamicallyAccessedMember.SourceContext, targetContext)) {
@@ -2144,7 +2197,7 @@ namespace Mono.Linker.Dataflow
MarkField (ref reflectionContext, field, DependencyKind.DynamicallyAccessedMember);
break;
case TypeDefinition nestedType:
- MarkNestedType (ref reflectionContext, nestedType, DependencyKind.DynamicallyAccessedMember);
+ MarkType (ref reflectionContext, nestedType, DependencyKind.DynamicallyAccessedMember);
break;
case PropertyDefinition property:
MarkProperty (ref reflectionContext, property, DependencyKind.DynamicallyAccessedMember);
@@ -2152,6 +2205,9 @@ namespace Mono.Linker.Dataflow
case EventDefinition @event:
MarkEvent (ref reflectionContext, @event, DependencyKind.DynamicallyAccessedMember);
break;
+ case InterfaceImplementation interfaceImplementation:
+ MarkInterfaceImplementation (ref reflectionContext, interfaceImplementation, DependencyKind.DynamicallyAccessedMember);
+ break;
case null:
var source = reflectionContext.Source;
reflectionContext.RecordRecognizedPattern (typeDefinition, () => _markStep.MarkEntireType (typeDefinition, includeBaseTypes: true, includeInterfaceTypes: true, new DependencyInfo (DependencyKind.DynamicallyAccessedMember, source), source));
@@ -2160,11 +2216,11 @@ namespace Mono.Linker.Dataflow
}
}
- void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference)
+ void MarkType (ref ReflectionPatternContext reflectionContext, TypeReference typeReference, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
{
var source = reflectionContext.Source;
TypeDefinition type = _context.TryResolveTypeDefinition (typeReference);
- reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (DependencyKind.AccessedViaReflection, source), source));
+ reflectionContext.RecordRecognizedPattern (type, () => _markStep.MarkTypeVisibleToReflection (typeReference, type, new DependencyInfo (dependencyKind, source), source));
}
void MarkMethod (ref ReflectionPatternContext reflectionContext, MethodDefinition method, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
@@ -2174,13 +2230,6 @@ namespace Mono.Linker.Dataflow
reflectionContext.RecordRecognizedPattern (method, () => _markStep.MarkMethodVisibleToReflection (method, new DependencyInfo (dependencyKind, source), new MessageOrigin (source, offset)));
}
- void MarkNestedType (ref ReflectionPatternContext reflectionContext, TypeDefinition nestedType, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
- {
- var source = reflectionContext.Source;
- TypeDefinition type = _context.TryResolveTypeDefinition (nestedType);
- reflectionContext.RecordRecognizedPattern (nestedType, () => _markStep.MarkTypeVisibleToReflection (nestedType, type, new DependencyInfo (dependencyKind, source), source));
- }
-
void MarkField (ref ReflectionPatternContext reflectionContext, FieldDefinition field, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
{
var source = reflectionContext.Source;
@@ -2213,6 +2262,12 @@ namespace Mono.Linker.Dataflow
});
}
+ void MarkInterfaceImplementation (ref ReflectionPatternContext reflectionContext, InterfaceImplementation interfaceImplementation, DependencyKind dependencyKind = DependencyKind.AccessedViaReflection)
+ {
+ var source = reflectionContext.Source;
+ reflectionContext.RecordRecognizedPattern (interfaceImplementation, () => _markStep.MarkInterfaceImplementation (interfaceImplementation, source, new DependencyInfo (dependencyKind, source)));
+ }
+
void MarkConstructorsOnType (ref ReflectionPatternContext reflectionContext, TypeDefinition type, Func<MethodDefinition, bool> filter, BindingFlags? bindingFlags = null)
{
foreach (var ctor in type.GetConstructorsOnType (filter, bindingFlags))
@@ -2231,7 +2286,7 @@ namespace Mono.Linker.Dataflow
foreach (var nestedType in type.GetNestedTypesOnType (filter, bindingFlags)) {
result.Add (nestedType);
- MarkNestedType (ref reflectionContext, nestedType);
+ MarkType (ref reflectionContext, nestedType);
}
return result.ToArray ();
diff --git a/src/linker/Linker.Dataflow/ReflectionPatternContext.cs b/src/linker/Linker.Dataflow/ReflectionPatternContext.cs
index 564406409..3b4e08e53 100644
--- a/src/linker/Linker.Dataflow/ReflectionPatternContext.cs
+++ b/src/linker/Linker.Dataflow/ReflectionPatternContext.cs
@@ -67,7 +67,7 @@ namespace Mono.Linker.Dataflow
}
#pragma warning restore CA1822
- public void RecordRecognizedPattern (IMemberDefinition accessedItem, Action mark)
+ public void RecordRecognizedPattern (IMetadataTokenProvider accessedItem, Action mark)
{
#if DEBUG
if (!_patternAnalysisAttempted)
diff --git a/src/linker/Linker.Steps/MarkStep.cs b/src/linker/Linker.Steps/MarkStep.cs
index 1a5ae1bc8..1997bc9d0 100644
--- a/src/linker/Linker.Steps/MarkStep.cs
+++ b/src/linker/Linker.Steps/MarkStep.cs
@@ -884,7 +884,7 @@ namespace Mono.Linker.Steps
}
}
- IEnumerable<IMemberDefinition> members;
+ IEnumerable<IMetadataTokenProvider> members;
if (dynamicDependency.MemberSignature is string memberSignature) {
members = DocumentationSignatureParser.GetMembersByDocumentationSignature (type, memberSignature, acceptName: true);
if (!members.Any ()) {
@@ -903,7 +903,7 @@ namespace Mono.Linker.Steps
MarkMembers (type, members, new DependencyInfo (DependencyKind.DynamicDependency, dynamicDependency.OriginalAttribute), context);
}
- void MarkMembers (TypeDefinition typeDefinition, IEnumerable<IMemberDefinition> members, DependencyInfo reason, IMemberDefinition sourceLocationMember)
+ void MarkMembers (TypeDefinition typeDefinition, IEnumerable<IMetadataTokenProvider> members, DependencyInfo reason, IMemberDefinition sourceLocationMember)
{
foreach (var member in members) {
switch (member) {
@@ -926,6 +926,9 @@ namespace Mono.Linker.Steps
MarkEvent (@event, reason);
MarkMethodsIf (@event.OtherMethods, m => true, reason, sourceLocationMember);
break;
+ case InterfaceImplementation interfaceType:
+ MarkInterfaceImplementation (interfaceType, sourceLocationMember, reason);
+ break;
case null:
MarkEntireType (typeDefinition, includeBaseTypes: true, includeInterfaceTypes: true, reason, sourceLocationMember);
break;
@@ -3267,16 +3270,16 @@ namespace Mono.Linker.Steps
return IsFullyPreserved (type);
}
- protected virtual void MarkInterfaceImplementation (InterfaceImplementation iface, TypeDefinition type)
+ protected internal virtual void MarkInterfaceImplementation (InterfaceImplementation iface, IMemberDefinition sourceLocation, DependencyInfo? reason = null)
{
if (Annotations.IsMarked (iface))
return;
// Blame the type that has the interfaceimpl, expecting the type itself to get marked for other reasons.
- MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface), type);
+ MarkCustomAttributes (iface, new DependencyInfo (DependencyKind.CustomAttribute, iface), sourceLocation);
// Blame the interface type on the interfaceimpl itself.
- MarkType (iface.InterfaceType, new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface), type);
- Annotations.MarkProcessed (iface, new DependencyInfo (DependencyKind.InterfaceImplementationOnType, type));
+ MarkType (iface.InterfaceType, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationInterfaceType, iface), sourceLocation);
+ Annotations.MarkProcessed (iface, reason ?? new DependencyInfo (DependencyKind.InterfaceImplementationOnType, sourceLocation));
}
//
diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs b/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs
index 9e9c484af..7f22b45c8 100644
--- a/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs
+++ b/test/Mono.Linker.Tests.Cases.Expectations/Helpers/DataFlowTypeExtensions.cs
@@ -37,6 +37,8 @@ namespace Mono.Linker.Tests.Cases.Expectations.Helpers
public static void RequiresNonPublicProperties ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] this Type type) { }
+ public static void RequiresInterfaces ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] this Type type) { }
+
public static void RequiresNone (this Type type) { }
}
}
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs
new file mode 100644
index 000000000..630fc3c14
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/GetInterfaceDataFlow.cs
@@ -0,0 +1,142 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more 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.Helpers;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class GetInterfaceDataFlow
+ {
+ public static void Main ()
+ {
+ GetInterface_Name.Test ();
+ GetInterface_Name_IgnoreCase.Test ();
+ }
+
+ class GetInterface_Name
+ {
+ [ExpectedWarning ("IL2070", nameof (Type.GetInterface), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.Interfaces))]
+ static void TestNoAnnotation (Type type)
+ {
+ type.GetInterface ("ITestInterface");
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestWithAnnotation ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type)
+ {
+ type.GetInterface ("ITestInterface").RequiresInterfaces ();
+ type.GetInterface ("ITestInterface").RequiresAll (); // Warns
+ }
+
+ static void TestWithAll ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type)
+ {
+ type.GetInterface ("ITestInterface").RequiresInterfaces ();
+ type.GetInterface ("ITestInterface").RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestKnownType ()
+ {
+ // Interfaces marking is transitive - meaning that it marks all interfaces in the entire hierarchy
+ // so the return value of GetInterface always has all interfaces on it already.
+ typeof (TestType).GetInterface ("ITestInterface").RequiresInterfaces ();
+ typeof (TestType).GetInterface ("ITestInterface").RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestMultipleValues (int p, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeWithAll)
+ {
+ Type type;
+ if (p == 0)
+ type = typeof (TestType);
+ else
+ type = typeWithAll;
+
+ type.GetInterface ("ITestInterface").RequiresInterfaces ();
+ type.GetInterface ("ITestInterface").RequiresAll (); // Warns since only one of the values is guaranteed All
+ }
+
+ public static void Test ()
+ {
+ TestNoAnnotation (typeof (TestType));
+ TestWithAnnotation (typeof (TestType));
+ TestWithAnnotation (typeof (ITestInterface));
+ TestWithAll (typeof (TestType));
+ TestKnownType ();
+ TestMultipleValues (0, typeof (TestType));
+ }
+ }
+
+ class GetInterface_Name_IgnoreCase
+ {
+ [ExpectedWarning ("IL2070", nameof (Type.GetInterface), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.Interfaces))]
+ static void TestNoAnnotation (Type type)
+ {
+ type.GetInterface ("ITestInterface", false);
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestWithAnnotation ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type)
+ {
+ type.GetInterface ("ITestInterface", false).RequiresInterfaces ();
+ type.GetInterface ("ITestInterface", false).RequiresAll (); // Warns
+ }
+
+ static void TestWithAll ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type)
+ {
+ type.GetInterface ("ITestInterface", false).RequiresInterfaces ();
+ type.GetInterface ("ITestInterface", false).RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestKnownType ()
+ {
+ // Interfaces marking is transitive - meaning that it marks all interfaces in the entire hierarchy
+ // so the return value of GetInterface always has all interfaces on it already.
+ typeof (TestType).GetInterface ("ITestInterface", false).RequiresInterfaces ();
+ typeof (TestType).GetInterface ("ITestInterface", false).RequiresAll ();
+ }
+
+ [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll), nameof (DynamicallyAccessedMemberTypes) + "." + nameof (DynamicallyAccessedMemberTypes.All))]
+ static void TestMultipleValues (int p, [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeWithAll)
+ {
+ Type type;
+ if (p == 0)
+ type = typeof (TestType);
+ else
+ type = typeWithAll;
+
+ type.GetInterface ("ITestInterface", false).RequiresInterfaces ();
+ type.GetInterface ("ITestInterface", false).RequiresAll (); // Warns since only one of the values is guaranteed All
+ }
+
+ public static void Test ()
+ {
+ TestNoAnnotation (typeof (TestType));
+ TestWithAnnotation (typeof (TestType));
+ TestWithAnnotation (typeof (ITestInterface));
+ TestWithAll (typeof (TestType));
+ TestKnownType ();
+ TestMultipleValues (0, typeof (TestType));
+ }
+ }
+
+ interface ITestInterface
+ {
+ }
+
+ class TestType : ITestInterface
+ {
+ }
+ }
+}
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs
index d0b51abf5..d28c3ed54 100644
--- a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypes.cs
@@ -42,6 +42,7 @@ namespace Mono.Linker.Tests.Cases.DataFlow
RequirePublicEvents (typeof (PublicEventsType));
RequireNonPublicEvents (typeof (NonPublicEventsType));
RequireAllEvents (typeof (AllEventsType));
+ RequireInterfaces (typeof (InterfacesType));
RequireAll (typeof (AllType));
RequireAll (typeof (RequireAllWithRecursiveTypeReferences));
}
@@ -1565,6 +1566,94 @@ namespace Mono.Linker.Tests.Cases.DataFlow
static public event EventHandler<EventArgs> HideStaticEvent;
}
+ [Kept]
+ private static void RequireInterfaces (
+ [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)]
+ [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))]
+ Type type)
+ {
+ }
+
+ [Kept]
+ interface IInterfaceOnBaseBase
+ {
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IInterfaceOnBaseBase))]
+ interface IInterfacesOnBase : IInterfaceOnBaseBase
+ {
+ void OnBaseInterfaceMethod ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IInterfacesOnBase))] // Interface implementations are collected across all base types, so this one has to be included as well
+ [KeptInterface (typeof (IInterfaceOnBaseBase))] // Roslyn adds transitively implemented interfaces automatically
+ class InterfacesBaseType : IInterfacesOnBase
+ {
+ public void OnBaseInterfaceMethod () { }
+ }
+
+ [Kept]
+ interface IInterfacesEmpty
+ {
+ }
+
+ [Kept]
+ interface IInterfacesWithMethods
+ {
+ void InterfaceMethod ();
+ }
+
+ [Kept]
+ interface IInterfaceGeneric<T>
+ {
+ void GenericMethod<U> (T t, U u);
+ }
+
+ [Kept]
+ interface IInterfacesBase
+ {
+ void BaseMethod ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IInterfacesBase))]
+ interface IInterfacesDerived : IInterfacesBase
+ {
+ void DerivedMethod ();
+ }
+
+ interface IInterfacesSuperDerived : IInterfacesDerived
+ {
+ void SuperDerivedMethod ();
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IInterfacesEmpty))]
+ [KeptInterface (typeof (IInterfacesWithMethods))]
+ [KeptInterface (typeof (IInterfacesBase))] // Roslyn adds transitively implemented interfaces automatically
+ [KeptInterface (typeof (IInterfacesDerived))]
+ [KeptInterface (typeof (IInterfaceGeneric<int>))]
+ [KeptBaseType (typeof (InterfacesBaseType))]
+ class InterfacesType : InterfacesBaseType, IInterfacesEmpty, IInterfacesWithMethods, IInterfacesDerived, IInterfaceGeneric<int>
+ {
+ public void InterfaceMethod ()
+ {
+ }
+
+ public void BaseMethod ()
+ {
+ }
+
+ public void DerivedMethod ()
+ {
+ }
+
+ public void GenericMethod<U> (int t, U u)
+ {
+ }
+ }
[Kept]
private static void RequireAll (
@@ -1575,7 +1664,29 @@ namespace Mono.Linker.Tests.Cases.DataFlow
}
[Kept]
- class AllBaseType
+ interface IAllBaseGenericInterface<T>
+ {
+ [Kept]
+ void BaseInterfaceMethod ();
+ [Kept]
+ void BaseDefaultMethod () { }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IAllBaseGenericInterface<Int64>))]
+ interface IAllDerivedInterface : IAllBaseGenericInterface<Int64>
+ {
+ [Kept]
+ void DerivedInterfaceMethod ();
+
+ [Kept]
+ void DerivedDefaultMethod () { }
+ }
+
+ [Kept]
+ [KeptInterface (typeof (IAllDerivedInterface))]
+ [KeptInterface (typeof (IAllBaseGenericInterface<Int64>))]
+ class AllBaseType : IAllDerivedInterface
{
// This is different from all of the above cases.
// All means really everything - so we include everything on base class as well - including private stuff
@@ -1639,6 +1750,11 @@ namespace Mono.Linker.Tests.Cases.DataFlow
public static void HideStaticMethod () { }
[Kept]
+ public void DerivedInterfaceMethod () { }
+ [Kept]
+ public void BaseInterfaceMethod () { }
+
+ [Kept]
[KeptBackingField]
static public bool PublicStaticPropertyOnBase { [Kept] get; [Kept] set; }
[Kept]
diff --git a/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs
new file mode 100644
index 000000000..a16ffd9ef
--- /dev/null
+++ b/test/Mono.Linker.Tests.Cases/DataFlow/MemberTypesRelationships.cs
@@ -0,0 +1,249 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more 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.Helpers;
+
+namespace Mono.Linker.Tests.Cases.DataFlow
+{
+ [SkipKeptItemsValidation]
+ [ExpectedNoWarnings]
+ public class MemberTypesRelationships
+ {
+ public static void Main ()
+ {
+ TestPublicParameterlessConstructor (typeof (TestType));
+ TestPublicConstructors (typeof (TestType));
+ TestNonPublicConstructors (typeof (TestType));
+ TestPublicMethods (typeof (TestType));
+ TestNonPublicMethods (typeof (TestType));
+ TestPublicFields (typeof (TestType));
+ TestNonPublicFields (typeof (TestType));
+ TestPublicNestedTypes (typeof (TestType));
+ TestNonPublicNestedTypes (typeof (TestType));
+ TestPublicProperties (typeof (TestType));
+ TestNonPublicProperties (typeof (TestType));
+ TestPublicEvents (typeof (TestType));
+ TestNonPublicEvents (typeof (TestType));
+ TestInterfaces (typeof (TestType));
+ TestAll (typeof (TestType));
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicParameterlessConstructor (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type type)
+ {
+ type.RequiresPublicParameterlessConstructor ();
+ type.RequiresPublicConstructors (); // Warns
+ type.RequiresNonPublicConstructors (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicMethods (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicConstructors (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] Type type)
+ {
+ type.RequiresPublicParameterlessConstructor ();
+ type.RequiresPublicConstructors ();
+ type.RequiresNonPublicConstructors (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicMethods (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicParameterlessConstructor))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicConstructors (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type)
+ {
+ type.RequiresPublicParameterlessConstructor (); // Warns
+ type.RequiresPublicConstructors (); // Warns
+ type.RequiresNonPublicConstructors ();
+ type.RequiresNone ();
+ type.RequiresPublicMethods (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicMethods (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type)
+ {
+ type.RequiresPublicMethods ();
+ type.RequiresNonPublicMethods (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicConstructors (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicMethods))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicMethods (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type)
+ {
+ type.RequiresPublicMethods (); // Warns
+ type.RequiresNonPublicMethods ();
+ type.RequiresNone ();
+ type.RequiresNonPublicConstructors (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicFields (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type type)
+ {
+ type.RequiresPublicFields ();
+ type.RequiresNonPublicFields (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicConstructors (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicFields (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] Type type)
+ {
+ type.RequiresPublicFields (); // Warns
+ type.RequiresNonPublicFields ();
+ type.RequiresNone ();
+ type.RequiresNonPublicConstructors (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicNestedTypes))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresInterfaces))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicNestedTypes (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] Type type)
+ {
+ type.RequiresPublicNestedTypes ();
+ type.RequiresNonPublicNestedTypes (); // Warns
+ type.RequiresNone ();
+ type.RequiresInterfaces (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicNestedTypes))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresInterfaces))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicNestedTypes (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] Type type)
+ {
+ type.RequiresPublicNestedTypes (); // Warns
+ type.RequiresNonPublicNestedTypes ();
+ type.RequiresNone ();
+ type.RequiresInterfaces (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicProperties))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicProperties (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type type)
+ {
+ type.RequiresPublicProperties ();
+ type.RequiresNonPublicProperties (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicFields (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicProperties))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicProperties (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type)
+ {
+ type.RequiresPublicProperties (); // Warns
+ type.RequiresNonPublicProperties ();
+ type.RequiresNone ();
+ type.RequiresNonPublicFields (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicEvents))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestPublicEvents (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] Type type)
+ {
+ type.RequiresPublicEvents ();
+ type.RequiresNonPublicEvents (); // Warns
+ type.RequiresNone ();
+ type.RequiresPublicFields (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicEvents))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicFields))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestNonPublicEvents (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicEvents)] Type type)
+ {
+ type.RequiresPublicEvents (); // Warns
+ type.RequiresNonPublicEvents ();
+ type.RequiresNone ();
+ type.RequiresNonPublicFields (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicNestedTypes))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresNonPublicNestedTypes))]
+ [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresAll))]
+ static void TestInterfaces (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type)
+ {
+ type.RequiresInterfaces ();
+ type.RequiresNone ();
+ type.RequiresPublicNestedTypes (); // Warns
+ type.RequiresNonPublicNestedTypes (); // Warns
+ type.RequiresAll (); // Warns
+ }
+
+ static void TestAll (
+ [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type)
+ {
+ type.RequiresAll ();
+ type.RequiresNone ();
+ type.RequiresPublicParameterlessConstructor ();
+ type.RequiresPublicConstructors ();
+ type.RequiresNonPublicConstructors ();
+ type.RequiresPublicMethods ();
+ type.RequiresNonPublicMethods ();
+ type.RequiresPublicFields ();
+ type.RequiresNonPublicFields ();
+ type.RequiresPublicNestedTypes ();
+ type.RequiresNonPublicNestedTypes ();
+ type.RequiresPublicProperties ();
+ type.RequiresNonPublicProperties ();
+ type.RequiresPublicEvents ();
+ type.RequiresNonPublicEvents ();
+ type.RequiresInterfaces ();
+ }
+
+ class TestType { }
+ }
+}
diff --git a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
index a4a4b1f64..bca2b26bb 100644
--- a/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
+++ b/test/Mono.Linker.Tests/TestCasesRunner/AssemblyChecker.cs
@@ -205,7 +205,7 @@ namespace Mono.Linker.Tests.TestCasesRunner
}
}
- Assert.IsEmpty (expectedInterfaces, $"Unexpected interfaces on {src}");
+ Assert.IsEmpty (expectedInterfaces, $"Expected interfaces were not found on {src}");
}
}