diff options
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}"); } } |