diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs index 31ec99f22285c..18246282ca012 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/DiagnosticUtilities.cs @@ -70,7 +70,7 @@ internal static bool TryGetRequiresAttribute(TypeSystemEntity member, string req decoded = ecmaMethod.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); break; case MetadataType type: - var ecmaType = type as EcmaType; + var ecmaType = type.GetTypeDefinition() as EcmaType; if (ecmaType == null) return false; decoded = ecmaType.GetDecodedCustomAttribute("System.Diagnostics.CodeAnalysis", requiresAttributeName); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs index 4ed23ceb4bbfc..8bb4badad9b70 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/FlowAnnotations.cs @@ -227,6 +227,10 @@ public static bool IsTypeInterestingForDataflow(TypeDesc type) if (type.IsWellKnownType(WellKnownType.String)) return true; + // ByRef over an interesting type is itself interesting + if (type is ByRefType byRefType) + type = byRefType.ParameterType; + if (!type.IsDefType) return false; @@ -584,7 +588,7 @@ protected override TypeAnnotations CreateValueFromKey(TypeDesc key) { if (typeGenericParameterAnnotations == null) typeGenericParameterAnnotations = new DynamicallyAccessedMemberTypes[ecmaType.Instantiation.Length]; - typeGenericParameterAnnotations[genericParameter.Index] = annotation; + typeGenericParameterAnnotations[genericParameterIndex] = annotation; } } } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs index 4fde62216e253..428d5658bedab 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/Dataflow/MethodBodyScanner.cs @@ -993,18 +993,7 @@ private void ScanIndirectStore( StackSlot valueToStore = PopUnknown(currentStack, 1, methodBody, offset); StackSlot destination = PopUnknown(currentStack, 1, methodBody, offset); - foreach (var uniqueDestination in destination.Value) - { - if (uniqueDestination is FieldValue fieldDestination) - { - HandleStoreField(methodBody, offset, fieldDestination, valueToStore.Value); - } - else if (uniqueDestination is MethodParameterValue parameterDestination) - { - HandleStoreParameter(methodBody, offset, parameterDestination, valueToStore.Value); - } - } - + StoreInReference(destination.Value, valueToStore.Value, methodBody, offset, locals, curBasicBlock); } /// @@ -1237,6 +1226,14 @@ private void HandleCall( ValueNodeList methodArguments = PopCallArguments(currentStack, calledMethod, callingMethodBody, isNewObj, offset, out newObjValue); + // Multi-dimensional array access is represented as a call to a special Get method on the array (runtime provided method) + // We don't track multi-dimensional arrays in any way, so return unknown value. + if (calledMethod is ArrayMethod { Kind: ArrayMethodKind.Get or ArrayMethodKind.Address }) + { + currentStack.Push(new StackSlot(UnknownValue.Instance)); + return; + } + var dereferencedMethodParams = new List(); foreach (var argument in methodArguments) dereferencedMethodParams.Add(DereferenceValue(argument, locals, ref interproceduralState)); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs index 3a8a1438b2bf2..3ce2085c08a71 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/DependencyAnalysis/CustomAttributeBasedDependencyAlgorithm.cs @@ -26,20 +26,20 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen MethodDefinition methodDef = reader.GetMethodDefinition(methodHandle); // Handle custom attributes on the method - AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, methodDef.GetCustomAttributes(), method); // Handle custom attributes on method parameters foreach (ParameterHandle parameterHandle in methodDef.GetParameters()) { Parameter parameter = reader.GetParameter(parameterHandle); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method); } // Handle custom attributes on generic method parameters foreach (GenericParameterHandle genericParameterHandle in methodDef.GetGenericParameters()) { GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, parameter.GetCustomAttributes(), method); } // We don't model properties and events as separate entities within the compiler, so ensuring @@ -59,7 +59,7 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen PropertyAccessors accessors = property.GetAccessors(); if (accessors.Getter == methodHandle || accessors.Setter == methodHandle) - AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, property.GetCustomAttributes(), new PropertyPseudoDesc((EcmaType)method.OwningType, propertyHandle)); } foreach (EventDefinitionHandle eventHandle in declaringType.GetEvents()) @@ -68,7 +68,7 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen EventAccessors accessors = @event.GetAccessors(); if (accessors.Adder == methodHandle || accessors.Remover == methodHandle || accessors.Raiser == methodHandle) - AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, method.Module, @event.GetCustomAttributes(), new EventPseudoDesc((EcmaType)method.OwningType, eventHandle)); } } } @@ -77,32 +77,32 @@ public static void AddDependenciesDueToCustomAttributes(ref DependencyList depen { MetadataReader reader = type.MetadataReader; TypeDefinition typeDef = reader.GetTypeDefinition(type.Handle); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, typeDef.GetCustomAttributes(), type); // Handle custom attributes on generic type parameters foreach (GenericParameterHandle genericParameterHandle in typeDef.GetGenericParameters()) { GenericParameter parameter = reader.GetGenericParameter(genericParameterHandle); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, type.EcmaModule, parameter.GetCustomAttributes(), type); } } public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaField field) { FieldDefinition fieldDef = field.MetadataReader.GetFieldDefinition(field.Handle); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, field.Module, fieldDef.GetCustomAttributes(), field); } public static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaAssembly assembly) { AssemblyDefinition asmDef = assembly.MetadataReader.GetAssemblyDefinition(); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, asmDef.GetCustomAttributes(), assembly); ModuleDefinition moduleDef = assembly.MetadataReader.GetModuleDefinition(); - AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes()); + AddDependenciesDueToCustomAttributes(ref dependencies, factory, assembly, moduleDef.GetCustomAttributes(), assembly); } - private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles) + private static void AddDependenciesDueToCustomAttributes(ref DependencyList dependencies, NodeFactory factory, EcmaModule module, CustomAttributeHandleCollection attributeHandles, TypeSystemEntity parent) { MetadataReader reader = module.MetadataReader; var mdManager = (UsageBasedMetadataManager)factory.MetadataManager; @@ -126,7 +126,7 @@ private static void AddDependenciesDueToCustomAttributes(ref DependencyList depe CustomAttributeValue decodedValue = attribute.DecodeValue(attributeTypeProvider); // Make a new list in case we need to abort. - var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue) ?? new DependencyList(); + var caDependencies = factory.MetadataManager.GetDependenciesForCustomAttribute(factory, constructor, decodedValue, parent) ?? new DependencyList(); caDependencies.Add(factory.ReflectableMethod(constructor), "Attribute constructor"); caDependencies.Add(factory.ConstructedTypeSymbol(constructor.OwningType), "Attribute type"); diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs index c0e4920707fec..dbd00ed14dabe 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/MetadataManager.cs @@ -854,7 +854,7 @@ public virtual void GetDependenciesDueToAccess(ref DependencyList dependencies, { } - public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue) + public virtual DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue, TypeSystemEntity parent) { return null; } diff --git a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs index ecde1aa53ec86..a1df9214acdc6 100644 --- a/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs +++ b/src/coreclr/tools/aot/ILCompiler.Compiler/Compiler/UsageBasedMetadataManager.cs @@ -702,12 +702,12 @@ public override void GetDependenciesDueToAccess(ref DependencyList dependencies, } } - public override DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue) + public override DependencyList GetDependenciesForCustomAttribute(NodeFactory factory, MethodDesc attributeCtor, CustomAttributeValue decodedValue, TypeSystemEntity parent) { bool scanReflection = (_generationOptions & UsageBasedMetadataGenerationOptions.ReflectionILScanning) != 0; if (scanReflection) { - return (new AttributeDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(attributeCtor))).ProcessAttributeDataflow(attributeCtor, decodedValue); + return (new AttributeDataFlow(Logger, factory, FlowAnnotations, new Logging.MessageOrigin(parent))).ProcessAttributeDataflow(attributeCtor, decodedValue); } return null; diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptPrivateImplementationDetails.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptPrivateImplementationDetails.cs new file mode 100644 index 0000000000000..a410eb2f50bbc --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptPrivateImplementationDetails.cs @@ -0,0 +1,17 @@ +// 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.Class)] + public sealed class KeptPrivateImplementationDetailsAttribute : KeptAttribute + { + public KeptPrivateImplementationDetailsAttribute (string methodName) + { + if (string.IsNullOrEmpty (methodName)) + throw new ArgumentException ("Value cannot be null or empty.", nameof (methodName)); + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ApplyTypeAnnotations.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ApplyTypeAnnotations.cs new file mode 100644 index 0000000000000..4576ba2baa776 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ApplyTypeAnnotations.cs @@ -0,0 +1,231 @@ +// 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.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + public class ApplyTypeAnnotations + { + public static void Main () + { + TestFromTypeOf (); + TestFromTypeGetTypeOverConstant (); + TestFromStringContantWithAnnotation (); + TestFromStringConstantWithGeneric (); + TestFromStringConstantWithGenericAndAssemblyQualified (); + TestFromStringConstantWithGenericAndAssemblyQualifiedInvalidAssembly (); + TestFromStringConstantWithGenericAndAssemblyQualifiedNonExistingAssembly (); + } + + [Kept] + static void TestFromTypeOf () + { + RequireCombination (typeof (FromTypeOfTestType)); + } + + [Kept] + class FromTypeOfTestType + { + [Kept] + public FromTypeOfTestType () { } + public FromTypeOfTestType (int i) { } + + [Kept] + public void PublicMethod () { } + private void PrivateMethod () { } + + [Kept] + public bool _publicField; + private bool _privateField; + + [Kept] + [KeptBackingField] + public bool PublicProperty { [Kept] get; [Kept] set; } + private bool PrivateProperty { get; set; } + } + + [Kept] + static void TestFromTypeGetTypeOverConstant () + { + RequireCombination (Type.GetType ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromTypeGetTypeOverConstantTestType")); + } + + [Kept] + class FromTypeGetTypeOverConstantTestType + { + [Kept] + public FromTypeGetTypeOverConstantTestType () { } + public FromTypeGetTypeOverConstantTestType (int i) { } + + [Kept] + public void PublicMethod () { } + private void PrivateMethod () { } + + [Kept] + public bool _publicField; + private bool _privateField; + + [Kept] + [KeptBackingField] + public bool PublicProperty { [Kept] get; [Kept] set; } + private bool PrivateProperty { get; set; } + } + + [Kept] + static void TestFromStringContantWithAnnotation () + { + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithAnnotationTestType"); + } + + [Kept] + class FromStringConstantWithAnnotationTestType + { + [Kept] + public FromStringConstantWithAnnotationTestType () { } + public FromStringConstantWithAnnotationTestType (int i) { } + + [Kept] + public void PublicMethod () { } + private void PrivateMethod () { } + + [Kept] + public bool _publicField; + private bool _privateField; + + [Kept] + [KeptBackingField] + public bool PublicProperty { [Kept] get; [Kept] set; } + private bool PrivateProperty { get; set; } + } + + [Kept] + private static void RequireCombination ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties)] + Type type) + { + } + + [Kept] + private static void RequireCombinationOnString ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicProperties)] + string typeName) + { + } + + [Kept] + class FromStringConstantWithGenericInner + { + } + + [Kept] + [KeptMember (".ctor()")] + class FromStringConstantWithGeneric + { + [Kept] + public T GetValue () { return default (T); } + } + + [Kept] + class FromStringConstantWithGenericInnerInner + { + [Kept] + public void Method () + { + } + + int unusedField; + } + + [Kept] + class FromStringConstantWithGenericInnerOne< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + T> + { + } + + [Kept] + class FromStringConstantWithGenericInnerTwo + { + void UnusedMethod () + { + } + } + + [Kept] + class FromStringConstantWitGenericInnerMultiDimArray + { + } + + [Kept] + class FromStringConstantWithMultiDimArray + { + public void UnusedMethod () { } + } + + [Kept] + [KeptMember (".ctor()")] + class FromStringConstantWithGenericTwoParameters + { + } + + [Kept] + static void TestFromStringConstantWithGeneric () + { + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInner]]"); + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericTwoParameters`2[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerOne`1[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerInner],Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericInnerTwo]"); + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGeneric`1[[Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWitGenericInnerMultiDimArray[,]]]"); + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithMultiDimArray[,]"); + } + + [Kept] + [KeptMember (".ctor()")] + class FromStringConstantWithGenericAndAssemblyQualified + { + [Kept] + public T GetValue () { return default (T); } + } + + [Kept] + static void TestFromStringConstantWithGenericAndAssemblyQualified () + { + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+FromStringConstantWithGenericAndAssemblyQualified`1[[Mono.Linker.Tests.Cases.Expectations.Assertions.KeptAttribute,Mono.Linker.Tests.Cases.Expectations]]"); + } + + class InvalidAssemblyNameType + { + } + + [Kept] + static void TestFromStringConstantWithGenericAndAssemblyQualifiedInvalidAssembly () + { + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+InvalidAssemblyNameType,Invalid/Assembly/Name"); + } + + class NonExistingAssemblyType + { + } + + [Kept] + static void TestFromStringConstantWithGenericAndAssemblyQualifiedNonExistingAssembly () + { + RequireCombinationOnString ("Mono.Linker.Tests.Cases.DataFlow.ApplyTypeAnnotations+InvalidAssemblyNameType,NonExistingAssembly"); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ArrayDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ArrayDataFlow.cs new file mode 100644 index 0000000000000..9ce584fb9b0b7 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ArrayDataFlow.cs @@ -0,0 +1,645 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + public class ArrayDataFlow + { + public static void Main () + { + TestArrayWithInitializerOneElementStaticType (); + TestArrayWithInitializerOneElementParameter (typeof (TestType)); + TestArrayWithInitializerMultipleElementsStaticType (); + TestArrayWithInitializerMultipleElementsMix (typeof (TestType)); + + TestArraySetElementOneElementStaticType (); + TestArraySetElementOneElementMix (); + TestArraySetElementOneElementMerged (); + TestArraySetElementOneElementParameter (typeof (TestType)); + TestArraySetElementMultipleElementsStaticType (); + TestMergedArrayElement (1); + TestArraySetElementMultipleElementsMix (typeof (TestType)); + + TestArraySetElementAndInitializerMultipleElementsMix (typeof (TestType)); + + TestGetElementAtUnknownIndex (); + TestMergedArrayElementWithUnknownIndex (0); + + // Array reset - certain operations on array are not tracked fully (or impossible due to unknown inputs) + // and sometimes the only valid thing to do is to reset the array to all unknowns as it's impossible + // to determine what the operation did to the array. So after the reset, everything in the array + // should be treated as unknown value. + TestArrayResetStoreUnknownIndex (); + TestArrayResetGetElementOnByRefArray (); + TestArrayResetAfterCall (); + TestArrayResetAfterAssignment (); + TestMultiDimensionalArray.Test (); + + WriteCapturedArrayElement.Test (); + + ConstantFieldValuesAsIndex.Test (); + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArrayWithInitializerOneElementStaticType () + { + Type[] arr = new Type[] { typeof (TestType) }; + arr[0].RequiresAll (); + arr[1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArrayWithInitializerOneElementParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + Type[] arr = new Type[] { type }; + arr[0].RequiresAll (); + arr[1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArrayWithInitializerMultipleElementsStaticType () + { + Type[] arr = new Type[] { typeof (TestType), typeof (TestType), typeof (TestType) }; + arr[0].RequiresAll (); + arr[1].RequiresAll (); + arr[2].RequiresAll (); + arr[3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArrayWithInitializerMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[] arr = new Type[] { typeof (TestType), typeof (TProperties), typeAll }; + arr[0].RequiresAll (); + arr[1].RequiresPublicProperties (); + arr[1].RequiresPublicFields (); // Should warn + arr[2].RequiresAll (); + arr[3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArraySetElementOneElementStaticType () + { + Type[] arr = new Type[1]; + arr[0] = typeof (TestType); + arr[0].RequiresAll (); + arr[1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll))] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestArraySetElementOneElementMix () + { + Type[] arr = new Type[1]; + if (string.Empty.Length == 0) + arr[0] = GetUnknownType (); + else + arr[0] = GetTypeWithPublicConstructors (); + arr[0].RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + // https://github.com/dotnet/linker/issues/2737 + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementOneElementMerged () + { + Type[] arr = new Type[1]; + arr[0] = string.Empty.Length == 0 ? GetUnknownType () : GetTypeWithPublicConstructors (); + arr[0].RequiresAll (); + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArraySetElementOneElementParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + Type[] arr = new Type[1]; + arr[0] = type; + arr[0].RequiresAll (); + arr[1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArraySetElementMultipleElementsStaticType () + { + Type[] arr = new Type[3]; + arr[0] = typeof (TestType); + arr[1] = typeof (TestType); + arr[2] = typeof (TestType); + arr[0].RequiresAll (); + arr[1].RequiresAll (); + arr[2].RequiresAll (); + arr[3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2072", nameof (ArrayDataFlow.GetMethods))] + [ExpectedWarning ("IL2072", nameof (ArrayDataFlow.GetFields))] + static void TestMergedArrayElement (int i) + { + Type[] arr = new Type[] { null }; + if (i == 1) + arr[0] = GetMethods (); + else + arr[0] = GetFields (); + arr[0].RequiresAll (); // Should warn - Methods/Fields does not have match annotations with All. + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type GetMethods () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetFields () => null; + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArraySetElementMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[] arr = new Type[3]; + arr[0] = typeof (TestType); + arr[1] = typeof (TProperties); + arr[2] = typeAll; + arr[0].RequiresAll (); + + arr[1].RequiresPublicProperties (); + arr[1].RequiresPublicFields (); // Should warn + arr[2].RequiresAll (); + arr[3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestArraySetElementAndInitializerMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[] arr = new Type[] { typeof (TestType), null, null }; + arr[1] = typeof (TProperties); + arr[2] = typeAll; + arr[0].RequiresAll (); + arr[1].RequiresPublicProperties (); + arr[1].RequiresPublicFields (); // Should warn + arr[2].RequiresAll (); + arr[3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void TestGetElementAtUnknownIndex (int i = 0) + { + Type[] arr = new Type[] { typeof (TestType) }; + arr[i].RequiresPublicFields (); + } + + // Trimmer code doesnt handle locals from different branches separetely, therefore merges incorrectly GetMethods with Unknown producing both warnings + [ExpectedWarning ("IL2072", nameof (ArrayDataFlow.GetMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestMergedArrayElementWithUnknownIndex (int i) + { + Type[] arr = new Type[] { null }; + if (i == 1) + arr[0] = GetMethods (); + else + arr[i] = GetFields (); + arr[0].RequiresAll (); // Should warn - there is an unknown value on fields therefore the merged value should be unknown + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void TestArrayResetStoreUnknownIndex (int i = 0) + { + Type[] arr = new Type[] { typeof (TestType) }; + arr[0].RequiresPublicProperties (); + + arr[i] = typeof (TestType); // Unknown index - we reset array to all unknowns + + arr[0].RequiresPublicFields (); // Warns + } + + // https://github.com/dotnet/linker/issues/2680 - analyzer doesn't reset array in this case + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // https://github.com/dotnet/linker/issues/2680 - analyzer doesn't reset array in this case + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayResetGetElementOnByRefArray (int i = 0) + { + Type[] arr = new Type[] { typeof (TestType), typeof (TestType) }; + arr[0].RequiresPublicProperties (); + + TakesTypeByRef (ref arr[0]); // Should reset index 0 - analyzer doesn't + arr[0].RequiresPublicMethods (); // Should warn - analyzer doesn't + arr[1].RequiresPublicMethods (); // Shouldn't warn + + TakesTypeByRef (ref arr[i]); // Reset - unknown index + arr[1].RequiresPublicFields (); // Warns + } + + static void TakesTypeByRef (ref Type type) { } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void TestArrayResetAfterCall () + { + Type[] arr = new Type[] { typeof (TestType) }; + arr[0].RequiresPublicProperties (); + + // Calling a method and passing the array will reset the array after the call + // This is necessary since the array is passed by referenced and its unknown + // what the method will do to the array + TakesTypesArray (arr); + arr[0].RequiresPublicFields (); // Warns + } + + static void TakesTypesArray (Type[] types) { } + + // https://github.com/dotnet/linker/issues/2680 + // [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields))] + static void TestArrayResetAfterAssignment () + { + Type[] arr = new Type[] { typeof (TestType) }; + arr[0].RequiresPublicProperties (); + + // Assigning the array out of the method means that others can modify it - for non-method-calls it's not very likely to cause problems + // because the only meaningful way this could work in the program is if some other thread accessed and modified the array + // but it's still better to be safe in this case. + _externalArray = arr; + + arr[0].RequiresPublicFields (); // Should warn + } + + static Type[] _externalArray; + + static class TestMultiDimensionalArray + { + public static void Test () + { + TestArrayWithInitializerOneElementStaticType (); + TestArrayWithInitializerOneElementParameter (typeof (TestType)); + TestArrayWithInitializerMultipleElementsStaticType (); + TestArrayWithInitializerMultipleElementsMix (typeof (TestType)); + + TestArraySetElementOneElementStaticType (); + TestArraySetElementOneElementParameter (typeof (TestType)); + TestArraySetElementMultipleElementsStaticType (); + TestArraySetElementMultipleElementsMix (typeof (TestType)); + + TestArraySetElementAndInitializerMultipleElementsMix (typeof (TestType)); + + TestGetElementAtUnknownIndex (); + + // Array reset - certain operations on array are not tracked fully (or impossible due to unknown inputs) + // and sometimes the only valid thing to do is to reset the array to all unknowns as it's impossible + // to determine what the operation did to the array. So after the reset, everything in the array + // should be treated as unknown value. + TestArrayResetStoreUnknownIndex (); + TestArrayResetGetElementOnByRefArray (); + TestArrayResetAfterCall (); + TestArrayResetAfterAssignment (); + + TestAddressOfElement (); + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayWithInitializerOneElementStaticType () + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayWithInitializerOneElementParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + Type[,] arr = new Type[,] { { type } }; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Below are because we do not handle Multi dimensional arrays + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayWithInitializerMultipleElementsStaticType () + { + Type[,] arr = new Type[,] { { typeof (TestType), typeof (TestType), typeof (TestType) } }; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresAll (); + arr[0, 2].RequiresAll (); + arr[0, 3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + // Bug + // [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Below are because we do not handle Multi dimensional arrays + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayWithInitializerMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[,] arr = new Type[,] { { typeof (TestType), typeof (TProperties), typeAll } }; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicProperties (); + arr[0, 1].RequiresPublicFields (); // Should warn + arr[0, 2].RequiresAll (); + arr[0, 3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementOneElementStaticType () + { + Type[,] arr = new Type[1, 1]; + arr[0, 0] = typeof (TestType); + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementOneElementParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + Type[,] arr = new Type[1, 1]; + arr[0, 0] = type; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Below are because we do not handle Multi dimensional arrays + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementMultipleElementsStaticType () + { + Type[,] arr = new Type[1, 3]; + arr[0, 0] = typeof (TestType); + arr[0, 1] = typeof (TestType); + arr[0, 2] = typeof (TestType); + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresAll (); + arr[0, 2].RequiresAll (); + arr[0, 3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + // Bug + // [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Below are because we do not handle Multi dimensional arrays + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[,] arr = new Type[1, 3]; + arr[0, 0] = typeof (TestType); + arr[0, 1] = typeof (TProperties); + arr[0, 2] = typeAll; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicProperties (); + arr[0, 1].RequiresPublicFields (); // Should warn + arr[0, 2].RequiresAll (); + arr[0, 3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + // Bug + // [ExpectedWarning ("IL2087", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Below are because we do not handle Multi dimensional arrays + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArraySetElementAndInitializerMultipleElementsMix<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] TProperties> ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type typeAll) + { + Type[,] arr = new Type[,] { { typeof (TestType), null, null } }; + arr[0, 1] = typeof (TProperties); + arr[0, 2] = typeAll; + arr[0, 0].RequiresAll (); + arr[0, 1].RequiresPublicProperties (); + arr[0, 1].RequiresPublicFields (); // Should warn + arr[0, 2].RequiresAll (); + arr[0, 3].RequiresPublicMethods (); // Should warn - unknown value at this index + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestGetElementAtUnknownIndex (int i = 0) + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, i].RequiresPublicFields (); + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayResetStoreUnknownIndex (int i = 0) + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, 0].RequiresPublicProperties (); + + arr[0, i] = typeof (TestType); // Unknown index - we reset array to all unknowns + + arr[0, 0].RequiresPublicFields (); // Warns + } + + // https://github.com/dotnet/linker/issues/2680 - analyzer doesn't reset array in this case + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayResetGetElementOnByRefArray (int i = 0) + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, 0].RequiresPublicProperties (); + + TakesTypeByRef (ref arr[0, 0]); // No reset - known index + arr[0, 0].RequiresPublicMethods (); // Doesn't warn + + TakesTypeByRef (ref arr[0, i]); // Reset - unknown index + arr[0, 0].RequiresPublicFields (); // Warns + } + + static void TakesTypeByRef (ref Type type) { } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayResetAfterCall () + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, 0].RequiresPublicProperties (); + + // Calling a method and passing the array will reset the array after the call + // This is necessary since the array is passed by referenced and its unknown + // what the method will do to the array + TakesTypesArray (arr); + arr[0, 0].RequiresPublicFields (); // Warns + } + + static void TakesTypesArray (Type[,] types) { } + + // https://github.com/dotnet/linker/issues/2680 + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Multidimensional Arrays not handled -- assumed to be UnknownValue + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicProperties), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestArrayResetAfterAssignment () + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + arr[0, 0].RequiresPublicProperties (); + + // Assigning the array out of the method means that others can modify it - for non-method-calls it's not very likely to cause problems + // because the only meaningful way this could work in the program is if some other thread accessed and modified the array + // but it's still better to be safe in this case. + _externalArray = arr; + + arr[0, 0].RequiresPublicFields (); // Should warn + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestAddressOfElement () + { + Type[,] arr = new Type[,] { { typeof (TestType) } }; + ref Type t = ref arr[0, 0]; + t.RequiresPublicMethods (); + } + + static Type[,] _externalArray; + } + + class WriteCapturedArrayElement + { + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + // https://github.com/dotnet/linker/issues/2737 + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestNullCoalesce () + { + Type[] arr = new Type[1]; + arr[0] = GetUnknownType () ?? GetTypeWithPublicConstructors (); + arr[0].RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll))] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNullCoalescingAssignment () + { + Type[] arr = new Type[1]; + arr[0] = GetTypeWithPublicConstructors (); + arr[0] ??= GetUnknownType (); + arr[0].RequiresAll (); + } + + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + // https://github.com/dotnet/linker/issues/2746 + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestNullCoalescingAssignmentToEmpty () + { + Type[] arr = new Type[1]; + arr[0] ??= GetUnknownType (); + arr[0].RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll))] + // https://github.com/dotnet/linker/issues/2746 (Linker produces incomplete set of IL2072 warnings) + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Analyzer)] + static void TestNullCoalescingAssignmentComplex () + { + Type[] arr = new Type[1]; + arr[0] = GetWithPublicMethods (); + arr[0] ??= (GetUnknownType () ?? GetTypeWithPublicConstructors ()); + arr[0].RequiresAll (); + } + + // Linker only incidentally matches the analyzer behavior here. + // https://github.com/dotnet/linker/issues/2737 + [ExpectedWarning ("IL2062", nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestNullCoalescingAssignmentToEmptyComplex () + { + Type[] arr = new Type[1]; + arr[0] ??= (GetUnknownType () ?? GetTypeWithPublicConstructors ()); + arr[0].RequiresAll (); + } + + public static void Test () + { + TestNullCoalesce (); + TestNullCoalescingAssignment (); + TestNullCoalescingAssignmentToEmpty (); + TestNullCoalescingAssignmentComplex (); + TestNullCoalescingAssignmentToEmptyComplex (); + } + } + + class ConstantFieldValuesAsIndex + { + private const sbyte ConstSByte = 1; + private const byte ConstByte = 1; + private const short ConstShort = 1; + private const ushort ConstUShort = 1; + private const int ConstInt = 1; + private const uint ConstUInt = 1; + // Longs and ULongs would need support for conversion logic, which is not implement yet + + public static void Test () + { + var types = new Type[2]; + types[0] = GetUnknownType (); + types[1] = typeof (TestType); + + // All the consts are 1, so there should be no warnings + types[ConstSByte].RequiresPublicMethods (); + types[ConstByte].RequiresPublicMethods (); + types[ConstShort].RequiresPublicMethods (); + types[ConstUShort].RequiresPublicMethods (); + types[ConstInt].RequiresPublicMethods (); + types[ConstUInt].RequiresPublicMethods (); + } + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + private static Type GetTypeWithPublicConstructors () + { + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + private static Type GetWithPublicMethods () + { + return null; + } + + private static Type GetUnknownType () + { + return null; + } + + public class TestType { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs index e2df9679801a4..eebc1fdd9e4d0 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AssemblyQualifiedNameDataflow.cs @@ -60,6 +60,7 @@ static void TestConstructors () RequireNothing (type); } + // NativeAOT doesn't implement this yet: https://github.com/dotnet/runtime/issues/72833 [ExpectedWarning ("IL2105", "Type 'System.Invalid.TypeName' was not found in the caller assembly nor in the base library. " + "Type name strings used for dynamically accessing a type should be assembly qualified.", diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs new file mode 100644 index 0000000000000..866af4714a747 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeConstructorDataflow.cs @@ -0,0 +1,144 @@ +// 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.Reflection; +using System.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [Kept] + [ExpectedNoWarnings] + class AttributeConstructorDataflow + { + [KeptAttributeAttribute (typeof (KeepsPublicConstructorAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicFieldsAttribute))] + [KeptAttributeAttribute (typeof (TypeArrayAttribute))] + [KeepsPublicConstructor (typeof (ClassWithKeptPublicConstructor))] + [KeepsPublicMethods ("Mono.Linker.Tests.Cases.DataFlow.AttributeConstructorDataflow+ClassWithKeptPublicMethods")] + [KeepsPublicFields (null, null)] + [TypeArray (new Type[] { typeof (AttributeConstructorDataflow) })] + // Trimmer only for now - https://github.com/dotnet/linker/issues/2273 + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void Main () + { + typeof (AttributeConstructorDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicConstructorAttribute)); + typeof (AttributeConstructorDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicMethodsAttribute)); + AllOnSelf.Test (); + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicConstructorAttribute : Attribute + { + [Kept] + public KeepsPublicConstructorAttribute ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type type) + { + } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicMethodsAttribute : Attribute + { + [Kept] + public KeepsPublicMethodsAttribute ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + string type) + { + } + } + + // Used to test null parameter values + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicFieldsAttribute : Attribute + { + [Kept] + public KeepsPublicFieldsAttribute ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + Type type, + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + string typeName) + { + } + } + + [Kept] + class ClassWithKeptPublicConstructor + { + [Kept] + public ClassWithKeptPublicConstructor (int unused) { } + + private ClassWithKeptPublicConstructor (short unused) { } + + public void Method () { } + } + + [Kept] + class ClassWithKeptPublicMethods + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--ClassWithKeptPublicMethods--")] + public static void KeptMethod () { } + static void Method () { } + } + + [Kept] + class AllOnSelf + { + [Kept] + public static void Test () + { + var t = typeof (KeepAllOnSelf); + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsAllAttribute : Attribute + { + [Kept] + public KeepsAllAttribute ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + } + + [KeepsAll (typeof (KeepAllOnSelf))] + [Kept] + [KeptAttributeAttribute (typeof (KeepsAllAttribute))] + [KeptMember (".ctor()")] + class KeepAllOnSelf + { + [Kept] + public void Method () { } + + [Kept] + public int Field; + } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class TypeArrayAttribute : Attribute + { + [Kept] + public TypeArrayAttribute (Type[] types) // This should not trigger data flow analysis of the parameter + { + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs new file mode 100644 index 0000000000000..80d182b031b53 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributeFieldDataflow.cs @@ -0,0 +1,118 @@ +// 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.Reflection; +using System.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [Kept] + [ExpectedNoWarnings] + class AttributeFieldDataflow + { + [KeptAttributeAttribute (typeof (KeepsPublicConstructorsAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicFieldsAttribute))] + [KeptAttributeAttribute (typeof (TypeArrayAttribute))] + [KeepsPublicConstructors (Type = typeof (ClassWithKeptPublicConstructor))] + [KeepsPublicMethods (Type = "Mono.Linker.Tests.Cases.DataFlow.AttributeFieldDataflow+ClassWithKeptPublicMethods")] + [KeepsPublicFields (Type = null, TypeName = null)] + [TypeArray (Types = new Type[] { typeof (AttributeFieldDataflow) })] + // Trimmer only for now - https://github.com/dotnet/linker/issues/2273 + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void Main () + { + typeof (AttributeFieldDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicConstructorsAttribute)); + typeof (AttributeFieldDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicMethodsAttribute)); + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicConstructorsAttribute : Attribute + { + [Kept] + public KeepsPublicConstructorsAttribute () + { + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + public Type Type; + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicMethodsAttribute : Attribute + { + [Kept] + public KeepsPublicMethodsAttribute () + { + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public string Type; + } + + // Use to test null values + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicFieldsAttribute : Attribute + { + [Kept] + public KeepsPublicFieldsAttribute () + { + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public Type Type; + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public string TypeName; + } + + [Kept] + class ClassWithKeptPublicConstructor + { + [Kept] + public ClassWithKeptPublicConstructor (int unused) { } + + private ClassWithKeptPublicConstructor (short unused) { } + + public void Method () { } + } + + [Kept] + class ClassWithKeptPublicMethods + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--ClassWithKeptPublicMethods--")] + public static void KeptMethod () { } + static void Method () { } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class TypeArrayAttribute : Attribute + { + [Kept] + public TypeArrayAttribute () + { + } + + [Kept] + public Type[] Types; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs new file mode 100644 index 0000000000000..4df62b35040eb --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/AttributePropertyDataflow.cs @@ -0,0 +1,123 @@ +// 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.Reflection; +using System.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [Kept] + [ExpectedNoWarnings] + class AttributePropertyDataflow + { + [KeptAttributeAttribute (typeof (KeepsPublicConstructorsAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicMethodsAttribute))] + [KeptAttributeAttribute (typeof (KeepsPublicFieldsAttribute))] + [KeptAttributeAttribute (typeof (TypeArrayAttribute))] + [KeepsPublicConstructors (Type = typeof (ClassWithKeptPublicConstructor))] + [KeepsPublicMethods (Type = "Mono.Linker.Tests.Cases.DataFlow.AttributePropertyDataflow+ClassWithKeptPublicMethods")] + [KeepsPublicFields (Type = null, TypeName = null)] + [TypeArray (Types = new Type[] { typeof (AttributePropertyDataflow) })] + // Trimmer only for now - https://github.com/dotnet/linker/issues/2273 + [ExpectedWarning ("IL2026", "--ClassWithKeptPublicMethods--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void Main () + { + typeof (AttributePropertyDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicConstructorsAttribute)); + typeof (AttributePropertyDataflow).GetMethod ("Main").GetCustomAttribute (typeof (KeepsPublicMethodsAttribute)); + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicConstructorsAttribute : Attribute + { + [Kept] + public KeepsPublicConstructorsAttribute () + { + } + + [field: Kept] + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + public Type Type { get; [Kept] set; } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicMethodsAttribute : Attribute + { + [Kept] + public KeepsPublicMethodsAttribute () + { + } + + [field: Kept] + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public string Type { get; [Kept] set; } + } + + // Used to test null values + [Kept] + [KeptBaseType (typeof (Attribute))] + class KeepsPublicFieldsAttribute : Attribute + { + [Kept] + public KeepsPublicFieldsAttribute () + { + } + + [field: Kept] + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public Type Type { get; [Kept] set; } + + [field: Kept] + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public string TypeName { get; [Kept] set; } + } + + [Kept] + class ClassWithKeptPublicConstructor + { + [Kept] + public ClassWithKeptPublicConstructor (int unused) { } + + private ClassWithKeptPublicConstructor (short unused) { } + + public void Method () { } + } + + [Kept] + class ClassWithKeptPublicMethods + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("--ClassWithKeptPublicMethods--")] + public static void KeptMethod () { } + static void Method () { } + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class TypeArrayAttribute : Attribute + { + [Kept] + public TypeArrayAttribute () + { + } + + [field: Kept] + [Kept] + public Type[] Types { get; [Kept] set; } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ByRefDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ByRefDataflow.cs new file mode 100644 index 0000000000000..72fcf82368b4c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ByRefDataflow.cs @@ -0,0 +1,154 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SetupCompileArgument ("/langversion:7.3")] + [SetupCompileArgument ("/unsafe")] + [Kept] + [ExpectedNoWarnings] + class ByRefDataflow + { + public static void Main () + { + { + Type t = typeof (ClassPassedToMethodTakingTypeByRef); + MethodWithRefParameter (ref t); + } + + { + Type t1 = typeof (ClassMaybePassedToMethodTakingTypeByRef); + Type t2 = typeof (OtherClassMaybePassedToMethodTakingTypeByRef); + ref Type t = ref t1; + if (string.Empty.Length == 0) + t = ref t2; + MethodWithRefParameter (ref t); + } + + PassRefToField (); + PassRefToParameter (null); + + PointerDereference.Test (); + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static Type s_typeWithPublicParameterlessConstructor; + + [Kept] + // Trimmer and analyzer use different formats for ref parameters: https://github.com/dotnet/linker/issues/2406 + [ExpectedWarning ("IL2077", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(Type&)", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2077", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(ref Type)", ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2069", nameof (s_typeWithPublicParameterlessConstructor), "parameter 'type'", nameof (MethodWithRefParameter), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // MethodWithRefParameter (ref x) + [ExpectedWarning ("IL2077", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(Type&)", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2077", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(ref Type)", ProducedBy = ProducedBy.Analyzer)] + public static void PassRefToField () + { + MethodWithRefParameter (ref s_typeWithPublicParameterlessConstructor); + var x = s_typeWithPublicParameterlessConstructor; + MethodWithRefParameter (ref x); + } + + [Kept] + // Trimmer and analyzer use different formats for ref parameters: https://github.com/dotnet/linker/issues/2406 + [ExpectedWarning ("IL2067", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(Type&)", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2067", nameof (ByRefDataflow) + "." + nameof (MethodWithRefParameter) + "(ref Type)", ProducedBy = ProducedBy.Analyzer)] + public static void PassRefToParameter (Type parameter) + { + MethodWithRefParameter (ref parameter); + } + + [Kept] + public static void MethodWithRefParameter ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + ref Type type) + { + type = typeof (ClassReturnedAsRefFromMethodTakingTypeByRef); + } + + [Kept] + class ClassPassedToMethodTakingTypeByRef + { + [Kept] + public static void KeptMethod () { } + internal static void RemovedMethod () { } + } + + [Kept] + class ClassReturnedAsRefFromMethodTakingTypeByRef + { + [Kept] + public static void KeptMethod () { } + internal static void RemovedMethod () { } + } + + [Kept] + class ClassMaybePassedToMethodTakingTypeByRef + { + [Kept] + public static void KeptMethod () { } + internal static void RemovedMethod () { } + } + + [Kept] + class OtherClassMaybePassedToMethodTakingTypeByRef + { + [Kept] + public static void KeptMethod () { } + internal static void RemovedMethod () { } + } + + [Kept] + unsafe class PointerDereference + { + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("")] + static unsafe void IntPtrDeref () + { + *_ptr = GetDangerous (); + } + + [Kept] + static IntPtr* _ptr; + + [Kept] + [KeptAttributeAttribute (typeof (RequiresUnreferencedCodeAttribute))] + [RequiresUnreferencedCode ("")] + static IntPtr GetDangerous () { return IntPtr.Zero; } + + [Kept] + [ExpectedWarning ("IL2070")] + static unsafe void LocalStackAllocDeref (Type t) + { + // Code pattern from CoreLib which caused problems in AOT port + // so making sure we handle this correctly (that is without failing) + int buffSize = 256; + byte* stackSpace = stackalloc byte[buffSize]; + byte* buffer = stackSpace; + + byte* toFree = buffer; + buffer = null; + + // IL2070 - this is to make sure that DataFlow ran on the method's body + t.GetProperties (); + } + + [Kept] + [ExpectedWarning ("IL2026")] + public static void Test () + { + IntPtrDeref (); + LocalStackAllocDeref (null); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs new file mode 100644 index 0000000000000..a36d4d9cc59e7 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedCodeDataflow.cs @@ -0,0 +1,582 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [SetupCompileArgument ("/unsafe")] + [ExpectedNoWarnings] + public class CompilerGeneratedCodeDataflow + { + public static void Main () + { + Iterator.Test (); + Async.Test (); + AsyncIterator.Test (); + LocalFunction.Test (); + Lambda.Test (); + Complex.Test (); + } + + class Iterator + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + CompilerGeneratedCode = true)] + static IEnumerable FlowAcrossYieldReturn () + { + Type t = GetWithPublicMethods (); + yield return 0; + t.RequiresAll (); + } + + // Linker tracks all assignments of hoisted locals, so this produces warnings. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), CompilerGeneratedCode = true)] + static IEnumerable NoFlowAcrossYieldReturn () + { + Type t = GetWithPublicMethods (); + t.RequiresPublicMethods (); + yield return 0; + t = GetWithPublicFields (); + t.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable UseParameterBeforeYieldReturn ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + publicMethodsType.RequiresAll (); + yield return 0; + } + + [ExpectedWarning ("IL2067", "unknownType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable UseUnannotatedParameterBeforeYieldReturn (Type unknownType = null) + { + unknownType.RequiresAll (); + yield return 0; + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable FlowParameterAcrossYieldReturn ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + yield return 0; + publicMethodsType.RequiresAll (); + } + + [ExpectedWarning ("IL2067", "unknownType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable FlowUnannotatedParameterAcrossYieldReturn (Type unknownType = null) + { + yield return 0; + unknownType.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + // Linker includes backwards branches for hoisted locals, by virtue of tracking all assignments. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static IEnumerable FlowAcrossYieldReturnWithBackwardsBranch (int n = 0) + { + Type t = GetWithPublicMethods (); + for (int i = 0; i < n; i++) { + yield return 0; + t.RequiresAll (); + yield return 1; + t = GetWithPublicFields (); + } + } + + public static void Test () + { + FlowAcrossYieldReturn ().GetEnumerator ().MoveNext (); // Has to call MoveNext otherwise AOT will actually remove it + NoFlowAcrossYieldReturn (); + NoFlowAcrossYieldReturn (); + UseParameterBeforeYieldReturn (); + UseUnannotatedParameterBeforeYieldReturn (); + FlowParameterAcrossYieldReturn (); + FlowUnannotatedParameterAcrossYieldReturn (); + FlowAcrossYieldReturnWithBackwardsBranch (); + } + } + + class Async + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + CompilerGeneratedCode = true)] + static async void FlowAcrossAwait () + { + Type t = GetWithPublicMethods (); + await MethodAsync (); + t.RequiresAll (); + } + + // Linker tracks all assignments of hoisted locals, so this produces warnings. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), CompilerGeneratedCode = true)] + static async void NoFlowAcrossAwait () + { + Type t = GetWithPublicMethods (); + t.RequiresPublicMethods (); + await MethodAsync (); + t = GetWithPublicFields (); + t.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2067", "publicMethodsType", nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true)] + static async void FlowParameterAcrossAwait ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type publicMethodsType = null) + { + await MethodAsync (); + publicMethodsType.RequiresAll (); + } + + public static void Test () + { + FlowAcrossAwait (); + NoFlowAcrossAwait (); + FlowParameterAcrossAwait (); + } + } + + class AsyncIterator + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static async IAsyncEnumerable FlowAcrossAwaitAndYieldReturn () + { + Type t = GetWithPublicMethods (); + await MethodAsync (); + yield return 0; + t.RequiresAll (); + } + + public static void Test () + { + FlowAcrossAwaitAndYieldReturn (); + } + } + + class LocalFunction + { + static void WarningsInBody () + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void LocalFunction () + { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + } + + LocalFunction (); + } + + static void WarningsInBodyUnused () + { + // Trimmer doesn't warn because this is unused code. + static void LocalFunction () + { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + } + } + + static void ReadCapturedVariable () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () + { + t.RequiresAll (); + } + } + + static void ReadMergedCapturedVariable (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + } else { + t = GetWithPublicFields (); + } + + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleBranches (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + LocalFunction (); + } else { + t = GetWithPublicFields (); + LocalFunction (); + } + + LocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleBranchesDistinct (bool b = false) + { + Type t; + if (b) { + t = GetWithPublicMethods (); + LocalFunctionRequiresMethods (); + } else { + t = GetWithPublicFields (); + LocalFunctionRequiresFields (); + } + + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresPublicFields), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunctionRequiresFields () => t.RequiresPublicFields (); + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunctionRequiresMethods () => t.RequiresPublicMethods (); + } + + static void ReadCapturedVariableWithBackwardsBranch (int i = 0) + { + Type t = GetWithPublicMethods (); + while (true) { + LocalFunction (); + if (i++ == 5) + break; + t = GetWithPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Linker includes backwards branches for hoisted locals, by virtue of tracking all assignments. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInMultipleFunctions () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + CallerOfLocalFunction (); + + void CallerOfLocalFunction () + { + t = GetWithPublicFields (); + LocalFunction (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + static void ReadCapturedVariableInCallGraphCycle () + { + Type t = GetUnknownType (); + A (); + + void A () + { + t = GetWithPublicMethods (); + LocalFunction (); + B (); + } + + void B () + { + t = GetWithPublicFields (); + LocalFunction (); + A (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + public static void ReadCapturedParameter (Type tParameter) + { + LocalFunction (); + + [ExpectedWarning ("IL2067", nameof (ReadCapturedParameter), "tParameter", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => tParameter.RequiresAll (); + } + + public static void ReadCapturedParameterAfterWrite (Type tParameter) + { + tParameter = GetWithPublicMethods (); + LocalFunction (); + + [ExpectedWarning ("IL2067", "tParameter", nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => tParameter.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + // analyzer clears all local state, but trimmer doesn't + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void ReadCapturedVariableWithUnhoistedLocals () + { + Type t = GetWithPublicMethods (); + Type notCaptured = GetWithPublicFields (); + LocalFunction (); + notCaptured.RequiresAll (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void WriteCapturedVariable () + { + Type t = GetWithPublicFields (); + LocalFunction (); + t.RequiresAll (); + + void LocalFunction () => t = GetWithPublicMethods (); + } + + public static void Test () + { + WarningsInBody (); + WarningsInBodyUnused (); + ReadCapturedVariable (); + ReadMergedCapturedVariable (); + ReadCapturedVariableInMultipleBranches (); + ReadCapturedVariableInMultipleBranchesDistinct (); + ReadCapturedVariableInMultipleFunctions (); + ReadCapturedVariableInCallGraphCycle (); + ReadCapturedVariableWithBackwardsBranch (); + ReadCapturedParameter (null); + ReadCapturedParameterAfterWrite (null); + ReadCapturedVariableWithUnhoistedLocals (); + WriteCapturedVariable (); + } + } + + class Lambda + { + static void WarningsInBody () + { + var lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + }; + + lambda (); + } + + static void WarningsInBodyUnused () + { + var lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => { + Type t = GetWithPublicMethods (); + t.RequiresAll (); + }; + } + + static void ReadCapturedVariable () + { + Type t = GetWithPublicMethods (); + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => t.RequiresAll (); + lambda (); + } + + static void ReadCapturedVariableAfterWriteAfterDefinition () + { + Type t = GetWithPublicFields (); + + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => t.RequiresAll (); + + t = GetWithPublicMethods (); + lambda (); + } + + public static void ReadCapturedParameter (Type tParameter) + { + var lambda = + [ExpectedWarning ("IL2067", nameof (ReadCapturedParameter), "tParameter", nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => tParameter.RequiresAll (); + + lambda (); + } + + public static void ReadCapturedParameterAfterWrite (Type tParameter) + { + tParameter = GetWithPublicMethods (); + var lambda = + // We produce dataflow warnings for the unknown parameter even though it has been overwritten + // with a value that satisfies the requirement. + [ExpectedWarning ("IL2067", "tParameter", nameof (DataFlowTypeExtensions.RequiresPublicMethods), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => tParameter.RequiresPublicMethods (); + lambda (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void ReadCapturedVariableWithUnhoistedLocals () + { + Type t = GetWithPublicMethods (); + Type notCaptured = GetWithPublicFields (); + Action lambda = + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => t.RequiresAll (); + lambda (); + notCaptured.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // We include all writes, including ones that can't reach the local function invocation. + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void WriteCapturedVariable () + { + Type t = GetWithPublicFields (); + Action lambda = () => t = GetWithPublicMethods (); + lambda (); + t.RequiresAll (); + } + + public static void Test () + { + WarningsInBody (); + WarningsInBodyUnused (); + ReadCapturedVariable (); + ReadCapturedVariableAfterWriteAfterDefinition (); + ReadCapturedParameter (null); + ReadCapturedParameterAfterWrite (null); + ReadCapturedVariableWithUnhoistedLocals (); + WriteCapturedVariable (); + } + } + + class Complex + { + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + // Linker merges branches going forward + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), CompilerGeneratedCode = true, + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static IEnumerable IteratorWithLocalFunctions () + { + Type t = GetWithPublicMethods (); + LocalFunction (); + + yield return 0; + + LocalFunction (); + t = GetWithPublicFields (); + LocalFunction (); + t.RequiresAll (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (GetWithPublicFields), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void LocalFunction () => t.RequiresAll (); + } + + static void NestedLocalFunctions () + { + Type t = GetWithPublicMethods (); + + OuterLocalFunction (); + + void OuterLocalFunction () + { + InnerLocalFunction (); + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (DataFlowTypeExtensions.RequiresAll), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + void InnerLocalFunction () + { + t.RequiresAll (); + } + } + } + + public static void Test () + { + IteratorWithLocalFunctions (); + NestedLocalFunctions (); + } + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + static Type GetAll () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type GetWithPublicMethods () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetWithPublicFields () => null; + + static Type GetUnknownType () => null; + + static async Task MethodAsync () + { + return await Task.FromResult (0); + } + + [RequiresUnreferencedCode ("RUC")] + static void RUCMethod () { } + + struct TestStruct + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public Type TypeWithPublicFields => null; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedTypes.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedTypes.cs new file mode 100644 index 0000000000000..09d476cedae9e --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/CompilerGeneratedTypes.cs @@ -0,0 +1,266 @@ +// 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.Reflection; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + class CompilerGeneratedTypes + { + public static void Main () + { + // Iterators + UseIterator (); + IteratorTypeMismatch (); + LocalIterator (); + IteratorCapture (); + NestedIterators (); + IteratorInsideClosure (); + IteratorInsideClosureMismatch (); + + // Async + Async (); + AsyncCapture (); + AsyncTypeMismatch (); + AsyncInsideClosure (); + AsyncInsideClosureMismatch (); + + // Closures + GlobalClosures (); + } + + private static void UseIterator () + { + foreach (var m in BasicIterator ()) { + } + } + + private static IEnumerable BasicIterator<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + foreach (var m in typeof (T).GetMethods ()) { + yield return m; + } + } + + private static void IteratorTypeMismatch () + { + _ = Local (); + + [ExpectedWarning ("IL2090", nameof (DynamicallyAccessedMemberTypes.PublicProperties), CompilerGeneratedCode = true)] + static IEnumerable Local<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + foreach (var m in typeof (T).GetMethods ()) { + yield return m; + } + foreach (var p in typeof (T).GetProperties ()) { + yield return p; + } + } + } + + private static void LocalIterator () + { + foreach (var m in Local ()) { } + + static IEnumerable Local< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + foreach (var m in typeof (T1).GetMethods ()) { + yield return m; + } + foreach (var p in typeof (T2).GetProperties ()) { + yield return p; + } + } + } + + private static void IteratorCapture () + { + Local1 (); + void Local1<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1> () + { + _ = Local2 (); + IEnumerable Local2<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + foreach (var m in typeof (T1).GetMethods ()) { + yield return m; + } + foreach (var p in typeof (T2).GetProperties ()) { + yield return p; + } + } + } + } + + private static void NestedIterators () + { + Local1 (); + IEnumerable Local1<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1> () + { + foreach (var o in Local2 ()) { yield return o; } + IEnumerable Local2<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + foreach (var m in typeof (T1).GetMethods ()) { + yield return m; + } + foreach (var p in typeof (T2).GetProperties ()) { + yield return p; + } + } + } + } + + private static void IteratorInsideClosure () + { + Outer (); + IEnumerable Outer<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1> () + { + int x = 0; + foreach (var o in Inner ()) yield return o; + IEnumerable Inner<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + x++; + foreach (var m in typeof (T1).GetMethods ()) yield return m; + foreach (var p in typeof (T2).GetProperties ()) yield return p; + } + } + } + + private static void IteratorInsideClosureMismatch () + { + Outer (); + + IEnumerable Outer<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T1> () + { + int x = 0; + foreach (var o in Inner ()) yield return o; + + [ExpectedWarning ("IL2090", "T1", "PublicMethods", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2090", "T2", "PublicProperties", CompilerGeneratedCode = true)] + IEnumerable Inner<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T2> () + { + x++; + foreach (var m in typeof (T1).GetMethods ()) yield return m; + foreach (var p in typeof (T2).GetProperties ()) yield return p; + } + } + + } + + private static void Async () + { + Local ().Wait (); + async Task Local<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + await Task.Delay (0); + _ = typeof (T).GetMethods (); + } + } + + private static void AsyncCapture () + { + Local1 (); + void Local1<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1> () + { + Local2 ().Wait (); + async Task Local2<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + await Task.Delay (0); + _ = typeof (T1).GetMethods (); + await Task.Delay (0); + _ = typeof (T2).GetProperties (); + } + } + } + + [ExpectedWarning ("IL2090", nameof (DynamicallyAccessedMemberTypes.PublicProperties), CompilerGeneratedCode = true)] + private static void AsyncTypeMismatch () + { + _ = Local (); + + static async Task Local<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { + await Task.Delay (0); + _ = typeof (T).GetMethods (); + await Task.Delay (0); + _ = typeof (T).GetProperties (); + } + } + + private static void AsyncInsideClosure () + { + Outer (); + void Outer<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T1> () + { + int x = 0; + Inner ().Wait (); + async Task Inner<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T2> () + { + await Task.Delay (0); + x++; + _ = typeof (T1).GetMethods (); + _ = typeof (T2).GetProperties (); + } + } + } + + private static void AsyncInsideClosureMismatch () + { + Outer (); + + void Outer<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] T1> () + { + int x = 0; + Inner ().Wait (); + + [ExpectedWarning ("IL2090", "T1", "PublicMethods", CompilerGeneratedCode = true)] + [ExpectedWarning ("IL2090", "T2", "PublicProperties", CompilerGeneratedCode = true)] + async Task Inner<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T2> () + { + await Task.Delay (0); + x++; + _ = typeof (T1).GetMethods (); + _ = typeof (T2).GetProperties (); + } + } + } + + private static void GlobalClosures () + { + GlobalClosureClass.M1 (); + GlobalClosureClass.M2 (); + } + + private sealed class GlobalClosureClass<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + public static void M1<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] U> () + { + Func a = (string s) => () => Console.WriteLine (s + typeof (T).GetMethods ()); + Func b = (string s) => + // https://github.com/dotnet/linker/issues/2826 + [ExpectedWarning ("IL2090", "U", "PublicProperties", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => Console.WriteLine (s + typeof (U).GetProperties ()); + a (""); + b (""); + } + public static void M2<[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] U> () + { + Func a = (string s) => () => Console.WriteLine (s + typeof (T).GetMethods ()); + Func b = (string s) => + // https://github.com/dotnet/linker/issues/2826 + [ExpectedWarning ("IL2090", "U", "PublicProperties", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + () => Console.WriteLine (s + typeof (U).GetProperties ()); + a (""); + b (""); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ComplexTypeHandling.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ComplexTypeHandling.cs new file mode 100644 index 0000000000000..2e071edd796dc --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ComplexTypeHandling.cs @@ -0,0 +1,196 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + public class ComplexTypeHandling + { + public static void Main () + { + TestArray (); + TestArrayOnGeneric (); + TestGenericArray (); + TestGenericArrayOnGeneric (); + TestArrayGetTypeFromMethodParam (); + TestArrayGetTypeFromField (); + TestArrayTypeGetType (); + TestArrayCreateInstanceByName (); + TestArrayInAttributeParameter (); + } + + [Kept] + class ArrayElementType + { + public ArrayElementType () { } + + public void PublicMethod () { } + + private int _privateField; + } + + [Kept] + static void TestArray () + { + RequirePublicMethods (typeof (ArrayElementType[])); + } + + [Kept] + static void TestGenericArray () + { + RequirePublicMethodsOnArrayOfGeneric (); + } + + [Kept] + static void RequirePublicMethodsOnArrayOfGeneric () + { + RequirePublicMethods (typeof (T[])); + } + + [Kept] + class ArrayElementInGenericType + { + public ArrayElementInGenericType () { } + + public void PublicMethod () { } + + private int _privateField; + } + + [Kept] + [KeptMember (".ctor()")] + class RequirePublicMethodsGeneric< + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + T> + { + } + + [Kept] + static void TestArrayOnGeneric () + { + _ = new RequirePublicMethodsGeneric (); + } + + [Kept] + static void TestGenericArrayOnGeneric () + { + RequirePublicMethodsOnArrayOfGenericParameter (); + } + + [Kept] + static void RequirePublicMethodsOnArrayOfGenericParameter () + { + _ = new RequirePublicMethodsGeneric (); + } + + [Kept] + sealed class ArrayGetTypeFromMethodParamElement + { + // This method should not be marked, instead Array.* should be marked + public void PublicMethod () { } + } + + [Kept] + static void TestArrayGetTypeFromMethodParamHelper (ArrayGetTypeFromMethodParamElement[] p) + { + RequirePublicMethods (p.GetType ()); + } + + [Kept] + static void TestArrayGetTypeFromMethodParam () + { + TestArrayGetTypeFromMethodParamHelper (null); + } + + [Kept] + sealed class ArrayGetTypeFromFieldElement + { + // This method should not be marked, instead Array.* should be marked + public void PublicMethod () { } + } + + [Kept] + static ArrayGetTypeFromFieldElement[] _arrayGetTypeFromField; + + [Kept] + static void TestArrayGetTypeFromField () + { + RequirePublicMethods (_arrayGetTypeFromField.GetType ()); + } + + [Kept] + sealed class ArrayTypeGetTypeElement + { + // This method should not be marked, instead Array.* should be marked + public void PublicMethod () { } + } + + [Kept] + static void TestArrayTypeGetType () + { + RequirePublicMethods (Type.GetType ("Mono.Linker.Tests.Cases.DataFlow.ComplexTypeHandling+ArrayTypeGetTypeElement[]")); + } + + // Technically there's no reason to mark this type since it's only used as an array element type and CreateInstance + // doesn't work on arrays, but the currently implementation will preserve it anyway due to how it processes + // string -> Type resolution. This will only impact code which would have failed at runtime, so very unlikely to + // actually occur in real apps (and even if it does happen, it just increases size, doesn't break behavior). + [Kept] + class ArrayCreateInstanceByNameElement + { + public ArrayCreateInstanceByNameElement () + { + } + } + + [Kept] + static void TestArrayCreateInstanceByName () + { + Activator.CreateInstance ("test", "Mono.Linker.Tests.Cases.DataFlow.ComplexTypeHandling+ArrayCreateInstanceByNameElement[]"); + } + + [Kept] + class ArrayInAttributeParamElement + { + // This method should not be marked, instead Array.* should be marked + public void PublicMethod () { } + } + + [Kept] + [KeptAttributeAttribute (typeof (RequiresPublicMethodAttribute))] + [RequiresPublicMethod (typeof (ArrayInAttributeParamElement[]))] + static void TestArrayInAttributeParameter () + { + } + + + [Kept] + private static void RequirePublicMethods ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + Type type) + { + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class RequiresPublicMethodAttribute : Attribute + { + [Kept] + public RequiresPublicMethodAttribute ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + Type t) + { + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/MemberTypesAllBaseTypeAssembly.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/MemberTypesAllBaseTypeAssembly.cs new file mode 100644 index 0000000000000..1e1a87958233e --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/MemberTypesAllBaseTypeAssembly.cs @@ -0,0 +1,49 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.DataFlow.Dependencies +{ + public class MemberTypesAllBaseType + { + static MemberTypesAllBaseType () { } + public MemberTypesAllBaseType () { } + private MemberTypesAllBaseType (bool _) { } + + public void PublicMethod () { } + private void PrivateMethod () { } + + public static void PublicStaticMethod () { } + private static void PrivateStaticMethod () { } + + public int PublicField; + private int PrivateField; + public static int PublicStaticField; + private static int PrivateStaticField; + + public bool PublicProperty { get; set; } + private bool PrivateProperty { get; set; } + public static bool PublicStaticProperty { get; set; } + private static bool PrivateStaticProperty { get; set; } + + public event EventHandler PublicEvent; + private event EventHandler PrivateEvent; + public static event EventHandler PublicStaticEvent; + private static event EventHandler PrivateStaticEvent; + + public class PublicNestedType + { + private void PrivateMethod () { } + } + + private class PrivateNestedType + { + private void PrivateMethod () { } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TestSystemTypeBase.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TestSystemTypeBase.cs new file mode 100644 index 0000000000000..55190581cc380 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/TestSystemTypeBase.cs @@ -0,0 +1,164 @@ +// 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.Globalization; +using System.Reflection; + +namespace System +{ + public class TestSystemTypeBase : Type + { + public override Assembly Assembly => throw new NotImplementedException (); + + public override string AssemblyQualifiedName => throw new NotImplementedException (); + + public override Type BaseType => throw new NotImplementedException (); + + public override string FullName => throw new NotImplementedException (); + + public override Guid GUID => throw new NotImplementedException (); + + public override Module Module => throw new NotImplementedException (); + + public override string Namespace => throw new NotImplementedException (); + + public override Type UnderlyingSystemType => throw new NotImplementedException (); + + public override string Name => throw new NotImplementedException (); + + public override ConstructorInfo[] GetConstructors (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override object[] GetCustomAttributes (bool inherit) + { + throw new NotImplementedException (); + } + + public override object[] GetCustomAttributes (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + public override Type GetElementType () + { + throw new NotImplementedException (); + } + + public override EventInfo GetEvent (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override EventInfo[] GetEvents (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override FieldInfo GetField (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override FieldInfo[] GetFields (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override Type GetInterface (string name, bool ignoreCase) + { + throw new NotImplementedException (); + } + + public override Type[] GetInterfaces () + { + throw new NotImplementedException (); + } + + public override MemberInfo[] GetMembers (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override MethodInfo[] GetMethods (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override Type GetNestedType (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override Type[] GetNestedTypes (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override PropertyInfo[] GetProperties (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override object InvokeMember (string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + throw new NotImplementedException (); + } + + public override bool IsDefined (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + protected override TypeAttributes GetAttributeFlagsImpl () + { + throw new NotImplementedException (); + } + + protected override ConstructorInfo GetConstructorImpl (BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + protected override MethodInfo GetMethodImpl (string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + protected override PropertyInfo GetPropertyImpl (string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + protected override bool HasElementTypeImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsArrayImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsByRefImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsCOMObjectImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsPointerImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsPrimitiveImpl () + { + throw new NotImplementedException (); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/UnresolvedLibrary.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/UnresolvedLibrary.cs new file mode 100644 index 0000000000000..c6ba7de56784a --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/Dependencies/UnresolvedLibrary.cs @@ -0,0 +1,16 @@ +// 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; + +namespace Mono.Linker.Tests.Cases.DataFlow.Dependencies +{ + public class UnresolvedType + { + public static void Method () { } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs new file mode 100644 index 0000000000000..029e5848b481b --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/DynamicDependencyDataflow.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + public class DynamicDependencyDataflow + { + public static void Main () + { + DynamicDependencyFrom (); + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type TypeWithPublicMethods; + + [Kept] + [ExpectedWarning ("IL2080", nameof (Type.GetField))] + [DynamicDependency ("DynamicDependencyTo")] + static void DynamicDependencyFrom () + { + _ = TypeWithPublicMethods.GetField ("f"); + } + + [Kept] + [ExpectedWarning ("IL2080", nameof (Type.GetProperty))] + static void DynamicDependencyTo () + { + _ = TypeWithPublicMethods.GetProperty ("p"); + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/EventDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/EventDataFlow.cs new file mode 100644 index 0000000000000..b1c637a737515 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/EventDataFlow.cs @@ -0,0 +1,48 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class EventDataFlow + { + public static void Main () + { + AssignToEvent.Test (); + } + + class AssignToEvent + { + static event EventHandler MyEvent; + + static void HandleMyEvent (object sender, EventArgs args) => throw null; + + static void HandleMyEvent2 (object sender, EventArgs args) => throw null; + + public static void TestAssignEvent () + { + MyEvent = HandleMyEvent; + } + + public static void TestAssignCapturedEvent (bool b = false) + { + MyEvent = b ? HandleMyEvent : HandleMyEvent2; + } + + // No dataflow warnings are involved, but the event assignment should not + // crash the analyzer. + public static void Test () + { + TestAssignEvent (); + TestAssignCapturedEvent (); + } + } + } +} \ No newline at end of file diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ExceptionalDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ExceptionalDataFlow.cs new file mode 100644 index 0000000000000..a29dfbfe8fd68 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/ExceptionalDataFlow.cs @@ -0,0 +1,992 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class ExceptionalDataFlow + { + public static void Main () + { + TryFlowsToFinally (); + TryFlowsToAfterFinally (); + MultipleTryExits (); + MultipleFinallyPaths (); + FinallyChain (); + FinallyChainWithPostFinallyState (); + TryFlowsToCatch (); + CatchFlowsToFinally (); + CatchFlowsToAfterTry (); + CatchFlowsToAfterFinally (); + FinallyFlowsToAfterFinally (); + TryFlowsToMultipleCatchAndFinally (); + NestedWithFinally (); + ControlFlowsOutOfMultipleFinally (); + NestedWithCatch (); + CatchInTry (); + CatchInTryWithFinally (); + TestCatchesHaveSeparateState (); + FinallyWithBranchToFirstBlock (); + FinallyWithBranchToFirstBlockAndEnclosingTryCatchState (); + CatchWithBranchToFirstBlock (); + CatchWithBranchToFirstBlockAndReassignment (); + CatchWithNonSimplePredecessor (); + FinallyWithNonSimplePredecessor (); + FinallyInTryWithPredecessor (); + NestedFinally (); + NestedFinallyWithPredecessor (); + ExceptionFilter (); + ExceptionFilterStateChange (); + ExceptionMultipleFilters (); + ExceptionFilterWithBranch (); + ExceptionFilterWithException (); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void TryFlowsToFinally () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } finally { + // methods/fields/properties + RequireAll (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void TryFlowsToAfterFinally () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } finally { + // prevent optimizing this away + _ = string.Empty; + } + // properties + RequireAll (t); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void MultipleTryExits () + { + Type t = GetWithPublicConstructors (); + for (int i = 0; i < 10; i++) { + try { + if (string.Empty.Length == 0) { + t = GetWithPublicMethods (); + return; + } + if (string.Empty.Length == 1) { + t = GetWithPublicFields (); + continue; + } + if (string.Empty.Length == 2) { + t = GetWithPublicProperties (); + break; + } + } finally { + RequireAll (t); + } + } + } + + // There are multiple paths through the finally to different subsequent blocks. + // On each path, only one state is possible, but we conservatively merge the (non-exceptional) + // finally states for each path and expect the warnings to reflect this merged state. + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicEvents) + "()", + ProducedBy = ProducedBy.Analyzer)] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicEvents) + "()")] + + [ExpectedWarning ("IL2073", nameof (MultipleFinallyPaths) + "()", nameof (GetWithPublicEvents) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2073", nameof (MultipleFinallyPaths) + "()", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2073", nameof (MultipleFinallyPaths) + "()", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2073", nameof (MultipleFinallyPaths) + "()", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + public static Type MultipleFinallyPaths () + { + Type t = GetWithPublicMethods (); // reaches RequireAll1 and RequireAll2 + while (true) { + RequireAll1 (t); + try { + if (string.Empty.Length == 1) { + t = GetWithPublicFields (); // reaches RequireAll1 and RequireAll2 + continue; + } + if (string.Empty.Length == 0) { + t = GetWithPublicProperties (); // reaches RequireAll2 only, but the finally mergig means + // the analysis thinks it can reach RequireAll1. + break; + } + if (string.Empty.Length == 2) { + t = GetWithPublicEvents (); // reaches return only, but the finally merging means + // the analysis thinks it can reach RequireAll1 (and hence RequireAll2). + return t; + } + } finally { + _ = string.Empty; + } + } + RequireAll2 (t); // properties + + throw new Exception (); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void FinallyChain () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + try { + t = GetWithPublicProperties (); + } finally { + RequireAll1 (t); // fields/properties + } + } finally { + RequireAll2 (t); // methods/fields/properties + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void FinallyChainWithPostFinallyState () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + try { + t = GetWithPublicProperties (); + } finally { + // normal: properties + // exception: fields/properties + RequireAll1 (t); // fields/properties + } + RequireAll2 (t); // properties + } finally { + // normal: properties + // exception: methods/fields/properties + RequireAll3 (t); // methods/fields/properties + } + RequireAll4 (t); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void TryFlowsToCatch () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } catch { + // methods/fields/properties + RequireAll (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void CatchFlowsToFinally () + { + Type t = GetWithPublicMethods (); + try { + } catch { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } finally { + // methods/fields/properties + RequireAll (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void CatchFlowsToAfterTry () + { + Type t = GetWithPublicMethods (); + try { + } catch { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } + // methods/properties, not fields + RequireAll (t); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void CatchFlowsToAfterFinally () + { + Type t = GetWithPublicMethods (); + try { + } catch { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } finally { } + // methods/properties, not fields + RequireAll (t); + } + + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void FinallyFlowsToAfterFinally () + { + Type t = GetWithPublicMethods (); + try { + } finally { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + } + // properties only + RequireAll (t); + } + + public class Exception1 : Exception { } + public class Exception2 : Exception { } + + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicEvents) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicProperties) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicEvents) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll7) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll7) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll7) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll7) + "(Type)", nameof (GetWithPublicEvents) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicEvents) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + + public static void TryFlowsToMultipleCatchAndFinally () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + RequireAll1 (t); // fields only + } catch (Exception1) { + RequireAll2 (t); // methods/fields + t = GetWithPublicProperties (); + RequireAll3 (t); // properties only + } catch (Exception2) { + RequireAll4 (t); // methods/fields + t = GetWithPublicEvents (); + RequireAll5 (t); // events only + } finally { + RequireAll6 (t); // methods/fields/properties/events + t = GetWithPublicConstructors (); + RequireAll7 (t); // ctors only + } + RequireAll (t); + } + + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicEvents) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + public static void NestedWithFinally () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + try { + // fields + t = GetWithPublicProperties (); + } finally { + // fields/properties + RequireAll1 (t); + t = GetWithPublicEvents (); + t = GetWithPublicConstructors (); + } + // ctors + RequireAll2 (t); + } finally { + // methods/fields/properties/events/constructors + RequireAll3 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicEvents) + "()")] + public static void ControlFlowsOutOfMultipleFinally () + { + Type t = GetWithPublicMethods (); + try { + try { + try { + t = GetWithPublicFields (); + } finally { + // methods/fields + RequireAll1 (t); + t = GetWithPublicProperties (); + } + } finally { + // methods/fields/properties + RequireAll2 (t); + t = GetWithPublicEvents (); + } + } finally { + // methods/fields/propreties/events + RequireAll3 (t); + } + } + + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicEvents) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + public static void NestedWithCatch () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + try { + // fields + t = GetWithPublicProperties (); + } catch { + // fields/properties + RequireAll1 (t); + t = GetWithPublicEvents (); + t = GetWithPublicConstructors (); + } + // properties/ctors + RequireAll2 (t); + } catch { + // methods/fields/properties/events/constructors + RequireAll3 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()")] + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void CatchInTry () + { + try { + Type t = GetWithPublicMethods (); + try { + } catch { + t = GetWithPublicFields (); + RequireAll (t); + } + } catch { + } + } + + // This tests a case where the catch state was being merged with the containing try state incorrectly. + // In the bug, the exceptional catch state, which is used in the finally, had too much in it. + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + // The bug was producing this warning: + // [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicConstructors) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void CatchInTryWithFinally () + { + Type t = GetWithPublicConstructors (); + try { + t = GetWithPublicMethods (); + // methods + // ex: ctors/methods + try { + // methods + // ex: methods + } catch { + // methods + t = GetWithPublicFields (); + // fields + // ex: methods/fields + RequireAll1 (t); + } finally { + // normal state: fields + // exceptional state: methods/fields + RequireAll2 (t); + } + } catch { + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void TestCatchesHaveSeparateState () + { + Type t = GetWithPublicMethods (); + try { + } catch (Exception1) { + t = GetWithPublicFields (); + } catch (Exception2) { + // methods only! + RequireAll (t); + } finally { + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + public static void FinallyWithBranchToFirstBlock () + { + Type t = GetWithPublicMethods (); + try { + } finally { + FinallyStart: + RequireAll (t); + t = GetWithPublicFields (); + goto FinallyStart; + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + public static void FinallyWithBranchToFirstBlockAndEnclosingTryCatchState () + { + try { + Type t = GetWithPublicProperties (); + t = GetWithPublicMethods (); + try { + } finally { + FinallyStart: + // methods/fields + RequireAll (t); + t = GetWithPublicFields (); + goto FinallyStart; + } + } finally { + // An operation just to prevent optimizing away + // the try/finally. + _ = String.Empty; + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + public static void CatchWithBranchToFirstBlock () + { + Type t = GetWithPublicMethods (); + try { + } catch { + CatchStart: + RequireAll (t); + t = GetWithPublicFields (); + goto CatchStart; + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + public static void CatchWithBranchToFirstBlockAndReassignment () + { + Type t = GetWithPublicMethods (); + try { + } catch { + CatchStart: + RequireAll (t); // methods/fields, but not properties! + t = GetWithPublicProperties (); + t = GetWithPublicFields (); + goto CatchStart; + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void CatchWithNonSimplePredecessor () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + try { + // properties only + } catch { + // properties only. + RequireAll1 (t); + } + } catch { + // methods/fields/properties + RequireAll2 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void FinallyWithNonSimplePredecessor () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + try { + // properties only + } catch { + // properties only. + RequireAll1 (t); + } + } finally { + // methods/fields/properties + RequireAll2 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + public static void FinallyInTryWithPredecessor () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + t = GetWithPublicProperties (); + try { + // properties only + } finally { + // properties only. + RequireAll1 (t); + } + } finally { + // methods/fields/properties + RequireAll2 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void NestedFinally () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + } finally { + try { + RequireAll1 (t); + t = GetWithPublicProperties (); + } finally { + RequireAll2 (t); + } + RequireAll3 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll1) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void NestedFinallyWithPredecessor () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + } finally { + _ = 0; // add an operation so that the try isn't the start of the finally. + try { + RequireAll1 (t); + t = GetWithPublicProperties (); + } finally { + RequireAll2 (t); + } + RequireAll3 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Analyzer)] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()")] + public static void ExceptionFilter () + { + Type t = GetWithPublicMethods (); + try { + t = GetWithPublicFields (); + } catch (Exception) when (RequireAllTrue (t)) { + RequireAll (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void ExceptionFilterStateChange () + { + Type t = GetWithPublicMethods (); + try { + } catch (Exception) when (RequireAllTrue (t = GetWithPublicFields ())) { + RequireAll (t); + } finally { + RequireAll2 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAllFalse) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicProperties) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll3) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAllFalse2) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAllFalse2) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAllFalse2) + "(Type)", nameof (GetWithPublicProperties) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll4) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll5) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicMethods) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicFields) + "()")] + [ExpectedWarning ("IL2072", nameof (RequireAll6) + "(Type)", nameof (GetWithPublicProperties) + "()")] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields) + "()", + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void ExceptionMultipleFilters () + { + Type t = GetWithPublicMethods (); + try { + } catch (Exception) when (RequireAllFalse (t = GetWithPublicFields ())) { + RequireAll (t); + } catch (Exception) when (RequireAllTrue (t = GetWithPublicProperties ())) { + RequireAll2 (t); + } catch (Exception1) { + RequireAll3 (t); + } catch (Exception) when (RequireAllFalse2 (t)) { + RequireAll4 (t); + } catch { + RequireAll5 (t); + } + RequireAll6 (t); + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties))] + + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicMethods))] + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicProperties))] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods))] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties))] + + // Linker merges branches going forward. + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + public static void ExceptionFilterWithBranch () + { + Type t = GetWithPublicMethods (); + try { + } catch (Exception) when (string.Empty.Length == 0 ? (t = GetWithPublicFields ()) == null : (t = GetWithPublicProperties ()) == null) { + RequireAll (t); + } catch (Exception) when (RequireAllTrue (t)) { + } catch { + RequireAll2 (t); + } + } + + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicMethods))] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAll) + "(Type)", nameof (GetWithPublicProperties))] + + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicMethods))] + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAllTrue) + "(Type)", nameof (GetWithPublicProperties))] + + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicMethods))] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicFields))] + [ExpectedWarning ("IL2072", nameof (RequireAll2) + "(Type)", nameof (GetWithPublicProperties))] + public static void ExceptionFilterWithException () + { + Type t = GetWithPublicMethods (); + try { + } catch (Exception) when ((t = GetWithPublicFields ()) != null + ? (t = GetWithPublicProperties ()) == null + : (t = GetWithPublicProperties ()) == null) { + } catch (Exception1) { + // An exception thrown from the above filter could result in methods, fields, or properties here, + // even though a non-exceptional exit from the filter always leaves t with 'properties'. + RequireAll (t); + } catch (Exception2) when (RequireAllTrue (t)) { + // Same as above, with a filter. + RequireAll2 (t); + } + } + + public static bool RequireAllTrue ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + return true; + } + + public static bool RequireAllFalse ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + return true; + } + + public static bool RequireAllFalse2 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + return true; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static Type GetWithPublicMethods () + { + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + public static Type GetWithPublicFields () + { + return null; + } + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] + public static Type GetWithPublicProperties () + { + return null; + } + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] + public static Type GetWithPublicEvents () + { + return null; + } + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + public static Type GetWithPublicConstructors () + { + return null; + } + + public static void RequireAll ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll1 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll2 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll3 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll4 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll5 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll6 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + public static void RequireAll7 ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + Type type) + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs new file mode 100644 index 0000000000000..280badd306629 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/FieldDataFlow.cs @@ -0,0 +1,317 @@ +// 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.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // Note: this test's goal is to validate that the product correctly reports unrecognized patterns + // - so the main validation is done by the ExpectedWarning attributes. + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class FieldDataFlow + { + public static void Main () + { + var instance = new FieldDataFlow (); + + instance.ReadFromInstanceField (); + instance.WriteToInstanceField (); + + instance.ReadFromStaticField (); + instance.WriteToStaticField (); + + instance.ReadFromInstanceFieldOnADifferentClass (); + instance.WriteToInstanceFieldOnADifferentClass (); + + instance.ReadFromStaticFieldOnADifferentClass (); + instance.WriteToStaticFieldOnADifferentClass (); + + instance.WriteUnknownValue (); + + WriteCapturedField.Test (); + + _ = _annotationOnWrongType; + + TestStringEmpty (); + + WriteArrayField.Test (); + AccessReturnedInstanceField.Test (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type _typeWithPublicParameterlessConstructor; + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static Type _staticTypeWithPublicParameterlessConstructor; + + static Type _staticTypeWithoutRequirements; + + [ExpectedWarning ("IL2097", nameof (_annotationOnWrongType))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static object _annotationOnWrongType; + + [ExpectedWarning ("IL2077", nameof (RequirePublicConstructors), + "_typeWithPublicParameterlessConstructor", "type", "RequirePublicConstructors(Type)")] + [ExpectedWarning ("IL2077", nameof (RequireNonPublicConstructors))] + private void ReadFromInstanceField () + { + RequirePublicParameterlessConstructor (_typeWithPublicParameterlessConstructor); + RequirePublicConstructors (_typeWithPublicParameterlessConstructor); + RequireNonPublicConstructors (_typeWithPublicParameterlessConstructor); + RequireNothing (_typeWithPublicParameterlessConstructor); + } + + [ExpectedWarning ("IL2074", nameof (FieldDataFlow) + "." + nameof (_typeWithPublicParameterlessConstructor), nameof (GetUnknownType))] + [ExpectedWarning ("IL2074", nameof (FieldDataFlow) + "." + nameof (_typeWithPublicParameterlessConstructor), nameof (GetTypeWithNonPublicConstructors))] + private void WriteToInstanceField () + { + _typeWithPublicParameterlessConstructor = GetTypeWithPublicParameterlessConstructor (); + _typeWithPublicParameterlessConstructor = GetTypeWithPublicConstructors (); + _typeWithPublicParameterlessConstructor = GetTypeWithNonPublicConstructors (); + _typeWithPublicParameterlessConstructor = GetUnknownType (); + } + + [ExpectedWarning ("IL2077", nameof (RequirePublicConstructors))] + [ExpectedWarning ("IL2077", nameof (RequireNonPublicConstructors))] + private void ReadFromInstanceFieldOnADifferentClass () + { + var store = new TypeStore (); + + RequirePublicParameterlessConstructor (store._typeWithPublicParameterlessConstructor); + RequirePublicConstructors (store._typeWithPublicParameterlessConstructor); + RequireNonPublicConstructors (store._typeWithPublicParameterlessConstructor); + RequireNothing (store._typeWithPublicParameterlessConstructor); + } + + [ExpectedWarning ("IL2074", nameof (TypeStore) + "." + nameof (TypeStore._typeWithPublicParameterlessConstructor), nameof (GetUnknownType))] + [ExpectedWarning ("IL2074", nameof (TypeStore) + "." + nameof (TypeStore._typeWithPublicParameterlessConstructor), nameof (GetTypeWithNonPublicConstructors))] + private void WriteToInstanceFieldOnADifferentClass () + { + var store = new TypeStore (); + + store._typeWithPublicParameterlessConstructor = GetTypeWithPublicParameterlessConstructor (); + store._typeWithPublicParameterlessConstructor = GetTypeWithPublicConstructors (); + store._typeWithPublicParameterlessConstructor = GetTypeWithNonPublicConstructors (); + store._typeWithPublicParameterlessConstructor = GetUnknownType (); + } + + [ExpectedWarning ("IL2077", nameof (RequirePublicConstructors))] + [ExpectedWarning ("IL2077", nameof (RequireNonPublicConstructors))] + private void ReadFromStaticField () + { + RequirePublicParameterlessConstructor (_staticTypeWithPublicParameterlessConstructor); + RequirePublicConstructors (_staticTypeWithPublicParameterlessConstructor); + RequireNonPublicConstructors (_staticTypeWithPublicParameterlessConstructor); + RequireNothing (_staticTypeWithPublicParameterlessConstructor); + } + + [ExpectedWarning ("IL2074", nameof (FieldDataFlow) + "." + nameof (_staticTypeWithPublicParameterlessConstructor), nameof (GetUnknownType))] + [ExpectedWarning ("IL2074", nameof (FieldDataFlow) + "." + nameof (_staticTypeWithPublicParameterlessConstructor), nameof (GetTypeWithNonPublicConstructors))] + [ExpectedWarning ("IL2079", nameof (FieldDataFlow) + "." + nameof (_staticTypeWithPublicParameterlessConstructor), nameof (_staticTypeWithoutRequirements))] + private void WriteToStaticField () + { + _staticTypeWithPublicParameterlessConstructor = GetTypeWithPublicParameterlessConstructor (); + _staticTypeWithPublicParameterlessConstructor = GetTypeWithPublicConstructors (); + _staticTypeWithPublicParameterlessConstructor = GetTypeWithNonPublicConstructors (); + _staticTypeWithPublicParameterlessConstructor = GetUnknownType (); + _staticTypeWithPublicParameterlessConstructor = _staticTypeWithoutRequirements; + } + + [ExpectedWarning ("IL2077", nameof (RequirePublicConstructors))] + [ExpectedWarning ("IL2077", nameof (RequireNonPublicConstructors))] + private void ReadFromStaticFieldOnADifferentClass () + { + RequirePublicParameterlessConstructor (TypeStore._staticTypeWithPublicParameterlessConstructor); + RequirePublicConstructors (TypeStore._staticTypeWithPublicParameterlessConstructor); + RequireNonPublicConstructors (TypeStore._staticTypeWithPublicParameterlessConstructor); + RequireNothing (TypeStore._staticTypeWithPublicParameterlessConstructor); + } + + [ExpectedWarning ("IL2074", nameof (TypeStore) + "." + nameof (TypeStore._staticTypeWithPublicParameterlessConstructor), nameof (GetUnknownType))] + [ExpectedWarning ("IL2074", nameof (TypeStore) + "." + nameof (TypeStore._staticTypeWithPublicParameterlessConstructor), nameof (GetTypeWithNonPublicConstructors))] + private void WriteToStaticFieldOnADifferentClass () + { + TypeStore._staticTypeWithPublicParameterlessConstructor = GetTypeWithPublicParameterlessConstructor (); + TypeStore._staticTypeWithPublicParameterlessConstructor = GetTypeWithPublicConstructors (); + TypeStore._staticTypeWithPublicParameterlessConstructor = GetTypeWithNonPublicConstructors (); + TypeStore._staticTypeWithPublicParameterlessConstructor = GetUnknownType (); + } + + [ExpectedWarning ("IL2064", nameof (TypeStore) + "." + nameof (TypeStore._staticTypeWithPublicParameterlessConstructor), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + private void WriteUnknownValue () + { + var array = new object[1]; + array[0] = this.GetType (); + MakeArrayValuesUnknown (array); + TypeStore._staticTypeWithPublicParameterlessConstructor = (Type) array[0]; + + static void MakeArrayValuesUnknown (object[] array) + { + } + } + + private static void TestStringEmpty () + { + RequirePublicMethods (string.Empty); + } + + class WriteCapturedField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + static Type field; + + [ExpectedWarning ("IL2074", nameof (GetUnknownType), nameof (field))] + [ExpectedWarning ("IL2074", nameof (GetTypeWithPublicConstructors), nameof (field))] + static void TestNullCoalesce () + { + field = GetUnknownType () ?? GetTypeWithPublicConstructors (); + } + + [ExpectedWarning ("IL2074", nameof (GetUnknownType), nameof (field))] + static void TestNullCoalescingAssignment () + { + field ??= GetUnknownType (); + } + + [ExpectedWarning ("IL2074", nameof (GetUnknownType), nameof (field))] + [ExpectedWarning ("IL2074", nameof (GetTypeWithPublicConstructors), nameof (field))] + static void TestNullCoalescingAssignmentComplex () + { + field ??= GetUnknownType () ?? GetTypeWithPublicConstructors (); + } + + public static void Test () + { + TestNullCoalesce (); + TestNullCoalescingAssignment (); + TestNullCoalescingAssignmentComplex (); + } + } + + class AccessReturnedInstanceField + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type field; + + static AccessReturnedInstanceField GetInstance ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type unused) => null; + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (GetInstance), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // https://github.com/dotnet/linker/issues/2832 + [ExpectedWarning ("IL2077", nameof (field), nameof (DataFlowTypeExtensions.RequiresAll))] + static void TestRead () + { + GetInstance (GetUnknownType ()).field.RequiresAll (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (GetInstance), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // https://github.com/dotnet/linker/issues/2832 + [ExpectedWarning ("IL2074", nameof (GetUnknownType), nameof (field))] + static void TestWrite () + { + GetInstance (GetUnknownType ()).field = GetUnknownType (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), nameof (GetInstance), + ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] // https://github.com/dotnet/linker/issues/2832 + [ExpectedWarning ("IL2074", nameof (GetUnknownType), nameof (field))] + static void TestNullCoalescingAssignment () + { + GetInstance (GetUnknownType ()).field ??= GetUnknownType (); + } + + public static void Test () + { + TestRead (); + TestWrite (); + TestNullCoalescingAssignment (); + } + } + + private static void RequirePublicMethods ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + string s) + { + } + + private static void RequirePublicParameterlessConstructor ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type) + { + } + + private static void RequirePublicConstructors ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type type) + { + } + + private static void RequireNonPublicConstructors ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + private static Type GetTypeWithPublicParameterlessConstructor () + { + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + private static Type GetTypeWithPublicConstructors () + { + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] + private static Type GetTypeWithNonPublicConstructors () + { + return null; + } + + private static Type GetUnknownType () + { + return null; + } + + private static void RequireNothing (Type type) + { + } + + class TypeStore + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + public Type _typeWithPublicParameterlessConstructor; + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + public static Type _staticTypeWithPublicParameterlessConstructor; + } + + class WriteArrayField + { + static Type[] ArrayField; + + static void TestAssignment () + { + ArrayField = Array.Empty (); + } + + static void TestCoalescingAssignment () + { + ArrayField ??= Array.Empty (); + } + + public static void Test () + { + TestAssignment (); + TestCoalescingAssignment (); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/IReflectDataflow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/IReflectDataflow.cs new file mode 100644 index 0000000000000..54ced51d123ca --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/IReflectDataflow.cs @@ -0,0 +1,174 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // Hits what appears to be a bug in the tool + // Could not initialize vtable of class(0x02000007) .MyReflect due to VTable setup of type Mono.Linker.Tests.Cases.DataFlow.IReflectDataflow+MyReflect failed assembly:/tmp/linker_tests/output/test.exe type:MyReflect member:(null) + [SkipPeVerify] + [ExpectedNoWarnings] + class IReflectDataflow + { + [ExpectBodyModified] + public static void Main () + { + // The cast here fails at runtime, but that's okay, we just want something to flow here. + // Casts are transparent to the dataflow analysis and preserve the tracked value. + RequirePublicParameterlessConstructor ((object) typeof (C1) as MyReflect); + s_requirePublicNestedTypes = ((object) typeof (C2)) as MyReflectDerived; + RequirePrivateMethods (typeof (C3)); + ReflectOverType.Test (); + } + + [Kept] + class C1 + { + [Kept] + public C1 () { } + public C1 (string s) { } + } + + [Kept] + class C2 + { + public C2 () { } + + [Kept] + [KeptMember (".ctor()")] + public class Nested { } + } + + [Kept] + class C3 + { + [Kept] + static void Foo () { } + public static void Bar () { } + } + + [Kept] + static void RequirePublicParameterlessConstructor ([KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute)), DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] MyReflect mine) + { + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] + static MyReflectDerived s_requirePublicNestedTypes; + + [Kept] + static void RequirePrivateMethods ([KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute)), DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] IReflect m) + { + } + + [Kept] + [KeptInterface (typeof (IReflect))] + [KeptMember (".ctor()")] + class MyReflect : IReflect + { + [Kept] + public Type UnderlyingSystemType { [Kept] get => throw new NotImplementedException (); } + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public FieldInfo GetField (string name, BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public FieldInfo[] GetFields (BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers ((DynamicallyAccessedMemberTypes) 8191)] + public MemberInfo[] GetMember (string name, BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers ((DynamicallyAccessedMemberTypes) 8191)] + public MemberInfo[] GetMembers (BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public MethodInfo GetMethod (string name, BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public MethodInfo GetMethod (string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public MethodInfo[] GetMethods (BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public PropertyInfo[] GetProperties (BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public PropertyInfo GetProperty (string name, BindingFlags bindingAttr) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + public PropertyInfo GetProperty (string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) => throw new NotImplementedException (); + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + public object InvokeMember (string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) => throw new NotImplementedException (); + } + + [Kept] + [KeptBaseType (typeof (MyReflect))] + class MyReflectDerived : MyReflect + { + } + + // This is effectively an E2E test for a situation encountered in https://github.com/dotnet/winforms/blob/main/src/System.Windows.Forms/src/System/Windows/Forms/HtmlToClrEventProxy.cs + // Validates that by using IReflect there's no escaping the annotations system. + [Kept] + class ReflectOverType + { + [Kept] + [KeptBaseType (typeof (MyReflect))] + class MyReflectOverType : MyReflect + { + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + Type _underlyingType; + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + IReflect _underlyingReflect; + + [Kept] + public MyReflectOverType ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + Type type) + { + _underlyingType = type; + _underlyingReflect = _underlyingType as IReflect; + } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public FieldInfo[] GetFields (BindingFlags bindingAttr) => _underlyingReflect.GetFields (bindingAttr); + } + + [Kept] + class TestType + { + [Kept] + public int Field; + } + + [Kept] + public static void Test () + { + new MyReflectOverType (typeof (TestType)).GetFields (BindingFlags.Instance | BindingFlags.Public); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/LocalDataFlowKeptMembers.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/LocalDataFlowKeptMembers.cs new file mode 100644 index 0000000000000..8e95a6ebcb172 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/LocalDataFlowKeptMembers.cs @@ -0,0 +1,252 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + public class LocalDataFlowKeptMembers + { + public static void Main () + { + // These behave as expected + TestBranchMergeGoto (); + TestBranchMergeIf (); + TestBranchMergeIfElse (); + TestBranchMergeSwitch (); + + // These are overly conservative (keep members on the wrong types) + // Only illustrate a few cases to keep it concise. + TestBranchGoto (); + TestBranchIf (); + TestBranchIfElse (); + } + + [Kept] + public static void TestBranchMergeGoto () + { + Type t = typeof (BranchMergeGotoType1); + if (String.Empty.Length == 0) + goto End; + t = typeof (BranchMergeGotoType2); + + End: + RequirePublicFields (t); // keeps fields for both types + } + + [Kept] + class BranchMergeGotoType1 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeGotoType2 + { + [Kept] + public string field; + } + + [Kept] + public static void TestBranchMergeIf () + { + Type t = typeof (BranchMergeIfType1); + if (String.Empty.Length == 0) + t = typeof (BranchMergeIfType2); + + RequirePublicFields (t); // keeps fields for both types + } + + [Kept] + class BranchMergeIfType1 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeIfType2 + { + [Kept] + public string field; + } + + [Kept] + public static void TestBranchMergeIfElse () + { + Type t = null; + if (String.Empty.Length == 0) { + t = typeof (BranchMergeIfElseType1); + } else { + t = typeof (BranchMergeIfElseType2); + } + RequirePublicFields (t); // keeps fields for both types + } + + [Kept] + class BranchMergeIfElseType1 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeIfElseType2 + { + [Kept] + public string field; + } + + [Kept] + static int _switchOnField; + + [Kept] + public static void TestBranchMergeSwitch () + { + Type t = null; + switch (_switchOnField) { + case 0: + t = typeof (BranchMergeSwitchType0); + break; + case 1: + t = typeof (BranchMergeSwitchType1); + break; + case 2: + t = typeof (BranchMergeSwitchType2); + break; + case 3: + t = typeof (BranchMergeSwitchType3); + break; + } + RequirePublicFields (t); // keeps fields for all types + } + + [Kept] + public static void TestBranchGoto () + { + Type t = typeof (BranchGotoType1); + if (String.Empty.Length == 0) + goto End; + t = typeof (BranchGotoType2); + RequirePublicFields (t); + End: + return; + } + + [Kept] + class BranchGotoType1 + { + [Kept] // unnecessary + public string field; + } + + [Kept] + class BranchGotoType2 + { + [Kept] + public string field; + } + + [Kept] + public static void TestBranchIf () + { + Type t = typeof (BranchIfType1); + if (String.Empty.Length == 0) { + t = typeof (BranchIfType2); + RequirePublicFields (t); + } + } + + [Kept] + class BranchIfType1 + { + [Kept] // unneccessary + public string field; + } + + [Kept] + class BranchIfType2 + { + [Kept] + public string field; + } + + [Kept] + public static void TestBranchIfElse () + { + Type t; + if (String.Empty.Length == 0) { + // because this branch *happens* to come first in IL, we will only see one value + t = typeof (BranchIfElseTypeWithMethods); + RequirePublicMethods (t); // this works + } else { + // because this branch *happens* to come second in IL, we will see the merged value for str + t = typeof (BranchIfElseTypeWithFields); + RequirePublicFields (t); // keeps field on BranchIfElseTypeWithMethods + } + } + + [Kept] + class BranchIfElseTypeWithMethods + { + [Kept] + public void Method () { } + [Kept] // unnecessary + public string field; + } + + [Kept] + class BranchIfElseTypeWithFields + { + public void Method () { } + [Kept] + public string field; + } + + [Kept] + class BranchMergeSwitchType0 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeSwitchType1 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeSwitchType2 + { + [Kept] + public string field; + } + + [Kept] + class BranchMergeSwitchType3 + { + [Kept] + public string field; + } + + + [Kept] + public static void RequirePublicFields ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] + Type type) + { + } + + [Kept] + public static void RequirePublicMethods ( + [KeptAttributeAttribute(typeof(DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + Type type) + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MemberTypesAllOnCopyAssembly.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MemberTypesAllOnCopyAssembly.cs new file mode 100644 index 0000000000000..775bfd2681aea --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MemberTypesAllOnCopyAssembly.cs @@ -0,0 +1,83 @@ +// 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.DataFlow.Dependencies; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + + [SetupCompileBefore ("base.dll", new[] { "Dependencies/MemberTypesAllBaseTypeAssembly.cs" })] + [KeptAssembly ("base.dll")] + [SetupLinkerAction ("link", "base")] + [SetupLinkerAction ("copy", "test")] + + [KeptTypeInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType")] + [KeptMemberInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType", new string[] { + ".cctor()", + ".ctor()", + ".ctor(System.Boolean)", + "PublicMethod()", + "PrivateMethod()", + "PublicStaticMethod()", + "PrivateStaticMethod()", + "PublicField", + "PrivateField", + "PublicStaticField", + "PrivateStaticField", + "PublicProperty", + "get_PublicProperty()", + "set_PublicProperty(System.Boolean)", + "PrivateProperty", + "get_PrivateProperty()", + "set_PrivateProperty(System.Boolean)", + "PublicStaticProperty", + "get_PublicStaticProperty()", + "set_PublicStaticProperty(System.Boolean)", + "PrivateStaticProperty", + "get_PrivateStaticProperty()", + "set_PrivateStaticProperty(System.Boolean)", + "PublicEvent", + "add_PublicEvent(System.EventHandler`1)", + "remove_PublicEvent(System.EventHandler`1)", + "PrivateEvent", + "add_PrivateEvent(System.EventHandler`1)", + "remove_PrivateEvent(System.EventHandler`1)", + "PublicStaticEvent", + "add_PublicStaticEvent(System.EventHandler`1)", + "remove_PublicStaticEvent(System.EventHandler`1)", + "PrivateStaticEvent", + "add_PrivateStaticEvent(System.EventHandler`1)", + "remove_PrivateStaticEvent(System.EventHandler`1)", + })] + + [KeptTypeInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType/PublicNestedType")] + [KeptMemberInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType/PublicNestedType", new string[] { "PrivateMethod()" })] + + [KeptTypeInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType/PrivateNestedType")] + [KeptMemberInAssembly ("base.dll", "Mono.Linker.Tests.Cases.DataFlow.Dependencies.MemberTypesAllBaseType/PrivateNestedType", new string[] { "PrivateMethod()" })] + + [KeptMember (".ctor()")] + public class MemberTypesAllOnCopyAssembly + { + public static void Main () + { + typeof (TestType).RequiresAll (); + } + + [Kept] + [KeptBaseType (typeof (MemberTypesAllBaseType))] + [KeptMember (".ctor()")] + class TestType : MemberTypesAllBaseType + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefParameterDataFlow.cs new file mode 100644 index 0000000000000..08e251d3f6ebf --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefParameterDataFlow.cs @@ -0,0 +1,372 @@ +// 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.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + class MethodByRefParameterDataFlow + { + public static void Main () + { + Type typeWithMethods = _fieldWithMethods; + + TestAssignStaticToAnnotatedRefParameter (ref typeWithMethods); + TestAssignParameterToAnnotatedRefParameter (ref typeWithMethods, typeof (TestType)); + + TestReadFromRefParameter (); + TestReadFromOutParameter_PassedTwice (); + TestReadFromRefParameter_MismatchOnOutput (); + TestReadFromRefParameter_MismatchOnOutput_PassedTwice (); + TestReadFromRefParameter_MismatchOnInput (); + TestReadFromRefParameter_MismatchOnInput_PassedTwice (); + Type nullType1 = null; + TestPassingRefParameter (ref nullType1); + Type nullType2 = null; + TestPassingRefParameter_Mismatch (ref nullType2); + Type nullType3 = null; + TestAssigningToRefParameter (nullType3, ref nullType3); + Type nullType4 = null; + TestAssigningToRefParameter_Mismatch (nullType4, ref nullType4); + TestPassingRefsWithImplicitThis (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type _fieldWithMethods = null; + + [ExpectedWarning ("IL2026", "Message for --TestType.Requires--")] + + // https://github.com/dotnet/linker/issues/2158 + // The type.GetMethods call generates a warning because we're not able to correctly track the value of the "this". + // (there's a ldind.ref insruction here which we currently don't handle and the "this" becomes unknown) + [ExpectedWarning ("IL2065")] + static void TestAssignStaticToAnnotatedRefParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type type) + { + type = typeof (TestTypeWithRequires); + type.GetMethods (); // Should not warn + } + + // The warning message is REALLY confusing (basically wrong) since it talks about "calling the method with wrong argument" + // which is definitely not the case here. + [ExpectedWarning ("IL2067", "typeWithFields")] + + // https://github.com/dotnet/linker/issues/2158 + // The type.GetMethods call generates a warning because we're not able to correctly track the value of the "this". + // (there's a ldind.ref insruction here which we currently don't handle and the "this" becomes unknown) + [ExpectedWarning ("IL2065")] + static void TestAssignParameterToAnnotatedRefParameter ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type type, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type typeWithFields) + { + type = typeWithFields; // Should warn + type.GetMethods (); // Should not warn + } + + class TestTypeWithRequires + { + [RequiresUnreferencedCode ("Message for --TestType.Requires--")] + public static void Requires () { } + } + + static void TestReadFromRefParameter () + { + Type typeWithMethods = null; + TryGetAnnotatedValue (ref typeWithMethods); + typeWithMethods.RequiresPublicMethods (); + } + + static void TestReadFromOutParameter_PassedTwice () + { + Type typeWithMethods = null; + TryGetAnnotatedValueFromValue (typeWithMethods, ref typeWithMethods); + typeWithMethods.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2067", nameof (TryGetAnnotatedValue), "RequiresPublicFields")] + static void TestReadFromRefParameter_MismatchOnOutput () + { + Type typeWithMethods = null; + TryGetAnnotatedValue (ref typeWithMethods); + typeWithMethods.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2067", nameof (TryGetAnnotatedValue), "RequiresPublicFields")] + static void TestReadFromRefParameter_MismatchOnOutput_PassedTwice () + { + Type typeWithMethods = null; + TryGetAnnotatedValueFromValue (typeWithMethods, ref typeWithMethods); + typeWithMethods.RequiresPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (TryGetAnnotatedValue))] + // https://github.com/dotnet/linker/issues/2632 + // This second warning should not be generated, the value of typeWithMethods should have PublicMethods + // after the call with out parameter. + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Analyzer)] + static void TestReadFromRefParameter_MismatchOnInput () + { + Type typeWithMethods = GetTypeWithFields (); + TryGetAnnotatedValue (ref typeWithMethods); + typeWithMethods.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (TryGetAnnotatedValueFromValue))] + [ExpectedWarning ("IL2072", nameof (TryGetAnnotatedValueFromValue))] + // https://github.com/dotnet/linker/issues/2632 + // This third warning should not be generated, the value of typeWithMethods should have PublicMethods + // after the call with ref parameter. + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresPublicMethods), ProducedBy = ProducedBy.Analyzer)] + static void TestReadFromRefParameter_MismatchOnInput_PassedTwice () + { + Type typeWithMethods = GetTypeWithFields (); + TryGetAnnotatedValueFromValue (typeWithMethods, ref typeWithMethods); + typeWithMethods.RequiresPublicMethods (); + } + + static void TestPassingRefParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type typeWithMethods) + { + TryGetAnnotatedValue (ref typeWithMethods); + } + + [ExpectedWarning ("IL2067", "typeWithMethods", nameof (TryGetAnnotatedValue))] + [ExpectedWarning ("IL2067", "typeWithMethods", nameof (TryGetAnnotatedValue))] + static void TestPassingRefParameter_Mismatch ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] ref Type typeWithMethods) + { + TryGetAnnotatedValue (ref typeWithMethods); + } + + static void TestAssigningToRefParameter ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type inputTypeWithMethods, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type outputTypeWithMethods) + { + outputTypeWithMethods = inputTypeWithMethods; + } + + [ExpectedWarning ("IL2067", "inputTypeWithFields", "outputTypeWithMethods")] + static void TestAssigningToRefParameter_Mismatch ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type inputTypeWithFields, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type outputTypeWithMethods) + { + outputTypeWithMethods = inputTypeWithFields; + } + + static bool TryGetAnnotatedValue ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type typeWithMethods) + { + typeWithMethods = null; + return false; + } + + static bool TryGetAnnotatedValueFromValue ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type inValue, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type typeWithMethods) + { + typeWithMethods = inValue; + return false; + } + + static void TestPassingRefsWithImplicitThis () + { + var x = new InheritsFromType (); + var param1 = typeof (int); + var param2 = typeof (string); + x.MethodWithRefAndImplicitThis (ref param1, in param2, out var param3); + param1.RequiresPublicMethods (); + param2.RequiresAll (); + param3.RequiresPublicFields (); + } + + [return: DynamicallyAccessedMembersAttribute (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetTypeWithFields () => null; + + class TestType + { + } + + class InheritsFromType : Type + { + public void MethodWithRefAndImplicitThis ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] ref Type type1, + in Type type2, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] out Type type3) + { + type3 = typeof (int); + } + public override Assembly Assembly => throw new NotImplementedException (); + + public override string AssemblyQualifiedName => throw new NotImplementedException (); + + public override Type BaseType => throw new NotImplementedException (); + + public override string FullName => throw new NotImplementedException (); + + public override Guid GUID => throw new NotImplementedException (); + + public override Module Module => throw new NotImplementedException (); + + public override string Namespace => throw new NotImplementedException (); + + public override Type UnderlyingSystemType => throw new NotImplementedException (); + + public override string Name => throw new NotImplementedException (); + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + public override ConstructorInfo[] GetConstructors (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + public override object[] GetCustomAttributes (bool inherit) + { + throw new NotImplementedException (); + } + + public override object[] GetCustomAttributes (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + public override Type GetElementType () + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo GetEvent (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override EventInfo[] GetEvents (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo GetField (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields)] + public override FieldInfo[] GetFields (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] + public override Type GetInterface (string name, bool ignoreCase) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] + public override Type[] GetInterfaces () + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.NonPublicFields | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicEvents | DynamicallyAccessedMemberTypes.NonPublicEvents)] + public override MemberInfo[] GetMembers (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + public override MethodInfo[] GetMethods (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public override Type GetNestedType (string name, BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes | DynamicallyAccessedMemberTypes.PublicNestedTypes)] + public override Type[] GetNestedTypes (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties | DynamicallyAccessedMemberTypes.PublicProperties)] + public override PropertyInfo[] GetProperties (BindingFlags bindingAttr) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] + public override object InvokeMember (string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + throw new NotImplementedException (); + } + + public override bool IsDefined (Type attributeType, bool inherit) + { + throw new NotImplementedException (); + } + + protected override TypeAttributes GetAttributeFlagsImpl () + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] + protected override ConstructorInfo GetConstructorImpl (BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] + protected override MethodInfo GetMethodImpl (string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] + protected override PropertyInfo GetPropertyImpl (string name, BindingFlags bindingAttr, Binder binder, Type returnType, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException (); + } + + protected override bool HasElementTypeImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsArrayImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsByRefImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsCOMObjectImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsPointerImpl () + { + throw new NotImplementedException (); + } + + protected override bool IsPrimitiveImpl () + { + throw new NotImplementedException (); + } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefReturnDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefReturnDataFlow.cs new file mode 100644 index 0000000000000..2766021fcf081 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodByRefReturnDataFlow.cs @@ -0,0 +1,85 @@ +// 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.Diagnostics.CodeAnalysis; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class MethodByRefReturnDataFlow + { + public static void Main () + { + ReturnAnnotatedTypeReferenceAsUnannotated (); + AssignToAnnotatedTypeReference (); + AssignDirectlyToAnnotatedTypeReference (); + AssignToCapturedAnnotatedTypeReference (); + AssignToAnnotatedTypeReferenceWithRequirements (); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type _annotatedField; + + // This should warn, as assiging to the return ref Type will assign value to the annotated field + // but the annotation is not propagated + // https://github.com/dotnet/linker/issues/2158 + static ref Type ReturnAnnotatedTypeReferenceAsUnannotated () { return ref _annotatedField; } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static ref Type ReturnAnnotatedTypeReferenceAsAnnotated () { return ref _annotatedField; } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static ref Type ReturnAnnotatedTypeWithRequirements ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type t) => ref _annotatedField; + + // Correct behavior in the linker, but needs to be added in analyzer + // Bug link: https://github.com/dotnet/linker/issues/2158 + [ExpectedWarning ("IL2026", "Message for --TestType.Requires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void AssignToAnnotatedTypeReference () + { + ref Type typeShouldHaveAllMethods = ref ReturnAnnotatedTypeReferenceAsAnnotated (); + typeShouldHaveAllMethods = typeof (TestTypeWithRequires); // This should apply the annotation -> cause IL2026 due to RUC method + _annotatedField.GetMethods (); // Doesn't warn, but now contains typeof(TestType) - no warning here is correct + } + + // Same as above for IL analysis, but this looks different to the Roslyn analyzer. + // https://github.com/dotnet/linker/issues/2158 + [ExpectedWarning ("IL2026", "Message for --TestType.Requires--", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void AssignDirectlyToAnnotatedTypeReference () + { + ReturnAnnotatedTypeReferenceAsAnnotated () = typeof (TestTypeWithRequires); + _annotatedField.GetMethods (); + } + + // https://github.com/dotnet/linker/issues/2158 + [ExpectedWarning ("IL2073", nameof (GetWithPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void AssignToCapturedAnnotatedTypeReference () + { + // In this testcase, the Roslyn analyzer sees an assignment to a flow-capture reference. + ReturnAnnotatedTypeReferenceAsAnnotated () = GetWithPublicMethods () ?? GetWithPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (GetWithPublicMethods), nameof (ReturnAnnotatedTypeWithRequirements))] + [ExpectedWarning ("IL2073", nameof (ReturnAnnotatedTypeWithRequirements), nameof (GetWithPublicFields), ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void AssignToAnnotatedTypeReferenceWithRequirements () + { + ReturnAnnotatedTypeWithRequirements (GetWithPublicMethods ()) = GetWithPublicFields (); + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type GetWithPublicMethods () => null; + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] + static Type GetWithPublicFields () => null; + + public class TestTypeWithRequires + { + [RequiresUnreferencedCode ("Message for --TestType.Requires--")] + public static void Requires () { } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs new file mode 100644 index 0000000000000..45a9320428782 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodParametersDataFlow.cs @@ -0,0 +1,288 @@ +// 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.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // Note: this test's goal is to validate that the product correctly reports unrecognized patterns + // - so the main validation is done by the ExpectedWarning attributes. + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + [SetupLinkerArgument ("--keep-metadata", "parametername")] + public class MethodParametersDataFlow + { + public static void Main () + { + var instance = new MethodParametersDataFlow (); + + PublicParameterlessConstructorParameter (typeof (TestType)); + PublicConstructorsParameter (typeof (TestType)); + NonPublicConstructorsParameter (typeof (TestType)); + WriteToParameterOnStaticMethod (null); + LongWriteToParameterOnStaticMethod (0, 0, 0, 0, null); + instance.InstanceMethod (typeof (TestType)); + instance.TwoAnnotatedParameters (typeof (TestType), typeof (TestType)); + instance.TwoAnnotatedParametersIntoOneValue (typeof (TestType), typeof (TestType)); + instance.NoAnnotation (typeof (TestType)); + instance.UnknownValue (); + instance.AnnotatedValueToUnAnnotatedParameter (typeof (TestType)); + instance.UnknownValueToUnAnnotatedParameter (); + instance.UnknownValueToUnAnnotatedParameterOnInterestingMethod (); + instance.WriteToParameterOnInstanceMethod (null); + instance.LongWriteToParameterOnInstanceMethod (0, 0, 0, 0, null); + instance.UnsupportedParameterType (null); + + ParametersPassedToInstanceCtor (typeof (TestType), typeof (TestType)); + + TestParameterOverwrite (typeof (TestType)); + + WriteCapturedParameter.Test (); + } + + // Validate the error message when annotated parameter is passed to another annotated parameter + [ExpectedWarning ("IL2067", "'sourceType'", "PublicParameterlessConstructorParameter(Type)", "'type'", "RequiresPublicConstructors(Type)")] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + private static void PublicParameterlessConstructorParameter ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type sourceType) + { + sourceType.RequiresPublicParameterlessConstructor (); + sourceType.RequiresPublicConstructors (); + sourceType.RequiresNonPublicConstructors (); + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + private static void PublicConstructorsParameter ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type type) + { + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + type.RequiresNonPublicConstructors (); + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicParameterlessConstructor))] + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + private static void NonPublicConstructorsParameter ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + type.RequiresNonPublicConstructors (); + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + private void InstanceMethod ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type) + { + type.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + } + + [ExpectedWarning ("IL2072", "'type'", "argument", nameof (WriteToParameterOnInstanceMethod) + "(Type)", nameof (ReturnThingsWithPublicParameterlessConstructor))] + private void WriteToParameterOnInstanceMethod ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + type = ReturnThingsWithPublicParameterlessConstructor (); + } + + [ExpectedWarning ("IL2072", "'type'", "argument", nameof (WriteToParameterOnStaticMethod) + "(Type)", nameof (ReturnThingsWithPublicParameterlessConstructor))] + private static void WriteToParameterOnStaticMethod ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + type = ReturnThingsWithPublicParameterlessConstructor (); + } + + [ExpectedWarning ("IL2072", "'type'", "argument", nameof (LongWriteToParameterOnInstanceMethod) + "(Int32, Int32, Int32, Int32, Type)", nameof (ReturnThingsWithPublicParameterlessConstructor))] + private void LongWriteToParameterOnInstanceMethod ( + int a, int b, int c, int d, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + type = ReturnThingsWithPublicParameterlessConstructor (); + } + + [ExpectedWarning ("IL2072", "'type'", "argument", nameof (LongWriteToParameterOnStaticMethod) + "(Int32, Int32, Int32, Int32, Type)", nameof (ReturnThingsWithPublicParameterlessConstructor))] + private static void LongWriteToParameterOnStaticMethod ( + int a, int b, int c, int d, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type type) + { + type = ReturnThingsWithPublicParameterlessConstructor (); + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + static private Type ReturnThingsWithPublicParameterlessConstructor () + { + return null; + } + + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + private void TwoAnnotatedParameters ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type type2) + { + type.RequiresPublicParameterlessConstructor (); + type2.RequiresPublicParameterlessConstructor (); + type.RequiresPublicConstructors (); + type2.RequiresPublicConstructors (); + } + + // TODO: https://github.com/dotnet/linker/issues/2273 + // (Dataflow analysis is not supported by the analyzer yet) + [ExpectedWarning ("IL2067", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors) + "(Type)", + "'type'")] + private void TwoAnnotatedParametersIntoOneValue ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type type2) + { + Type t = type == null ? type : type2; + t.RequiresPublicParameterlessConstructor (); + t.RequiresPublicConstructors (); + } + + // Validate the error message for the case of unannotated method return value passed to an annotated parameter. + [ExpectedWarning ("IL2067", "'type'", "NoAnnotation(Type)", "'type'", "RequiresPublicParameterlessConstructor(Type)")] + private void NoAnnotation (Type type) + { + type.RequiresPublicParameterlessConstructor (); + } + + // Validate error message when untracable value is passed to an annotated parameter. + [ExpectedWarning ("IL2062", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicParameterlessConstructor) + "(Type)", + "'type'", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + private void UnknownValue () + { + var array = new object[1]; + array[0] = this.GetType (); + MakeArrayValuesUnknown (array); + ((Type) array[0]).RequiresPublicParameterlessConstructor (); + + static void MakeArrayValuesUnknown (object[] array) + { + } + } + + private void AnnotatedValueToUnAnnotatedParameter ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type) + { + type.RequiresNone (); + } + + private void UnknownValueToUnAnnotatedParameter () + { + this.GetType ().RequiresNone (); + } + + private void UnknownValueToUnAnnotatedParameterOnInterestingMethod () + { + RequirePublicParameterlessConstructorAndNothing (typeof (TestType), this.GetType ()); + } + + [ExpectedWarning ("IL2098", "p1", nameof (UnsupportedParameterType))] + private void UnsupportedParameterType ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] object p1) + { + } + + private class InstanceCtor + { + [ExpectedWarning ("IL2067", nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + public InstanceCtor ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type type) + { + type.RequiresNonPublicConstructors (); + type.RequiresPublicConstructors (); + } + } + + [ExpectedWarning ("IL2067", "'type'")] + static void ParametersPassedToInstanceCtor ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type typeWithCtors, Type typeWithNothing) + { + var a1 = new InstanceCtor (typeWithCtors); // no warn + var a2 = new InstanceCtor (typeof (TestType)); // no warn + var a3 = new InstanceCtor (typeWithNothing); // warn + } + + private static void RequirePublicParameterlessConstructorAndNothing ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type type, + Type type2) + { + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static Type _fieldWithMethods; + + [ExpectedWarning ("IL2077", nameof (_fieldWithMethods))] + static void TestParameterOverwrite ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type type) + { + type = _fieldWithMethods; + type.GetFields (); + } + + class WriteCapturedParameter + { + [ExpectedWarning ("IL2072", nameof (GetUnknownType), "parameter")] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), "parameter")] + static void TestNullCoalesce ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type parameter = null) + { + parameter = GetUnknownType () ?? GetTypeWithPublicConstructors (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), "parameter")] + static void TestNullCoalescingAssignment ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type parameter = null) + { + parameter ??= GetUnknownType (); + } + + [ExpectedWarning ("IL2072", nameof (GetUnknownType), "parameter")] + [ExpectedWarning ("IL2072", nameof (GetTypeWithPublicConstructors), "parameter")] + static void TestNullCoalescingAssignmentComplex ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type parameter = null) + { + parameter ??= (GetUnknownType () ?? GetTypeWithPublicConstructors ()); + } + + public static void Test () + { + TestNullCoalesce (); + TestNullCoalescingAssignment (); + TestNullCoalescingAssignmentComplex (); + } + } + + class TestType + { + public TestType () { } + public TestType (int arg) { } + private TestType (int arg1, int arg2) { } + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + private static Type GetTypeWithPublicConstructors () + { + return null; + } + + private static Type GetUnknownType () + { + return null; + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs new file mode 100644 index 0000000000000..4fb7010b5d792 --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodReturnParameterDataFlow.cs @@ -0,0 +1,200 @@ +// 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.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + // Note: this test's goal is to validate that the product correctly reports unrecognized patterns + // - so the main validation is done by the ExpectedWarning attributes. + [SkipKeptItemsValidation] + [ExpectedNoWarnings] + public class MethodReturnParameterDataFlow + { + public static void Main () + { + var instance = new MethodReturnParameterDataFlow (); + + // Validation that assigning value to the return value is verified + NoRequirements (); + instance.ReturnPublicParameterlessConstructor (typeof (TestType), typeof (TestType), typeof (TestType)); + instance.ReturnPublicParameterlessConstructorFromUnknownType (null); + instance.ReturnPublicParameterlessConstructorFromConstant (); + instance.ReturnPublicParameterlessConstructorFromNull (); + instance.ReturnPublicConstructorsFailure (null); + instance.ReturnNonPublicConstructorsFailure (null); + instance.ReturnUnknownValue (); + + // Validation that value comming from return value of a method is correctly propagated + instance.PropagateReturnPublicParameterlessConstructor (); + instance.PropagateReturnPublicParameterlessConstructorFromConstant (); + instance.PropagateReturnToReturn (0); + + instance.ReturnWithRequirementsAlwaysThrows (); + + UnsupportedReturnType (); + } + + static Type NoRequirements () + { + return typeof (TestType); + } + + [ExpectedWarning ("IL2068", nameof (ReturnPublicParameterlessConstructor))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type ReturnPublicParameterlessConstructor ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type publicParameterlessConstructorType, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type publicConstructorsType, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type nonPublicConstructorsType) + { + switch (GetHashCode ()) { + case 1: + return publicParameterlessConstructorType; + case 2: + return publicConstructorsType; + case 3: + return nonPublicConstructorsType; + case 4: + return typeof (TestType); + default: + return null; + } + } + + [ExpectedWarning ("IL2068", nameof (ReturnPublicParameterlessConstructorFromUnknownType))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type ReturnPublicParameterlessConstructorFromUnknownType (Type unknownType) + { + return unknownType; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type ReturnPublicParameterlessConstructorFromConstant () + { + return typeof (TestType); + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type ReturnPublicParameterlessConstructorFromNull () + { + return null; + } + + Type ReturnTypeWithNoRequirements () + { + return null; + } + + // Validate error message when insufficiently annotated value is returned from a method + [ExpectedWarning ("IL2068", + "publicParameterlessConstructorType", + "MethodReturnParameterDataFlow.ReturnPublicConstructorsFailure", + "MethodReturnParameterDataFlow.ReturnPublicConstructorsFailure")] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + Type ReturnPublicConstructorsFailure ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type publicParameterlessConstructorType) + { + return publicParameterlessConstructorType; + } + + [ExpectedWarning ("IL2068", nameof (ReturnNonPublicConstructorsFailure))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] + Type ReturnNonPublicConstructorsFailure ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + Type publicConstructorsType) + { + return publicConstructorsType; + } + + [ExpectedWarning ("IL2063", + nameof (MethodReturnParameterDataFlow) + "." + nameof (ReturnUnknownValue) + "()", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + Type ReturnUnknownValue () + { + var array = new object[1]; + array[0] = this.GetType (); + MakeArrayValuesUnknown (array); + return (Type) array[0]; + + static void MakeArrayValuesUnknown (object[] array) + { + } + } + + [ExpectedWarning ("IL2072", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors) + "(Type)", + nameof (MethodReturnParameterDataFlow) + "." + nameof (ReturnPublicParameterlessConstructor) + "(Type, Type, Type)")] + [ExpectedWarning ("IL2072", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors) + "(Type)", + nameof (MethodReturnParameterDataFlow) + "." + nameof (ReturnPublicParameterlessConstructor) + "(Type, Type, Type)")] + void PropagateReturnPublicParameterlessConstructor () + { + Type t = ReturnPublicParameterlessConstructor (typeof (TestType), typeof (TestType), typeof (TestType)); + t.RequiresPublicParameterlessConstructor (); + t.RequiresPublicConstructors (); + t.RequiresNonPublicConstructors (); + t.RequiresNone (); + } + + [ExpectedWarning ("IL2072", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors) + "(Type)", + nameof (MethodReturnParameterDataFlow) + "." + nameof (ReturnPublicParameterlessConstructorFromConstant) + "()")] + [ExpectedWarning ("IL2072", + nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors) + "(Type)", + nameof (MethodReturnParameterDataFlow) + "." + nameof (ReturnPublicParameterlessConstructorFromConstant) + "()")] + void PropagateReturnPublicParameterlessConstructorFromConstant () + { + Type t = ReturnPublicParameterlessConstructorFromConstant (); + t.RequiresPublicParameterlessConstructor (); + t.RequiresPublicConstructors (); + t.RequiresNonPublicConstructors (); + t.RequiresNone (); + } + + [ExpectedWarning ("IL2073", + nameof (ReturnTypeWithNoRequirements), + nameof (PropagateReturnToReturn))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type PropagateReturnToReturn (int n) + { + switch (n) { + case 0: + return ReturnPublicParameterlessConstructorFromConstant (); + case 1: + return ReturnTypeWithNoRequirements (); + } + + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type ReturnWithRequirementsAlwaysThrows () + { + throw new NotImplementedException (); + } + + [ExpectedWarning ("IL2106", nameof (UnsupportedReturnType))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static object UnsupportedReturnType () => null; + + [ExpectedWarning ("IL2106", nameof (UnsupportedReturnTypeAndParameter))] + [ExpectedWarning ("IL2098", nameof (UnsupportedReturnTypeAndParameter))] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static object UnsupportedReturnTypeAndParameter ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] object param) => null; + + class TestType + { + public TestType () { } + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs new file mode 100644 index 0000000000000..f76e7e5b6827c --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/MethodThisDataFlow.cs @@ -0,0 +1,266 @@ +// 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.Globalization; +using System.Reflection; +using System.Text; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [SandboxDependency ("Dependencies/TestSystemTypeBase.cs")] + [ExpectedNoWarnings] + public class MethodThisDataFlow + { + public static void Main () + { + new MethodThisDataFlowTypeTest (); + + PropagateToThis (); + PropagateToThisWithGetters (); + PropagateToThisWithSetters (); + AssignToThis (); + + TestAnnotationOnNonTypeMethod (); + TestUnknownThis (); + TestFromParameterToThis (null); + TestFromFieldToThis (); + TestFromThisToOthers (); + TestFromGenericParameterToThis (); + } + + [ExpectedWarning ("IL2075", + "Mono.Linker.Tests.Cases.DataFlow.MethodThisDataFlow.GetWithNonPublicMethods()", + "System.MethodThisDataFlowTypeTest.RequireThisPublicMethods()")] + [ExpectedWarning ("IL2075", nameof (MethodThisDataFlowTypeTest.RequireThisNonPublicMethods))] + static void PropagateToThis () + { + GetWithPublicMethods ().RequireThisPublicMethods (); + GetWithNonPublicMethods ().RequireThisPublicMethods (); + + GetWithPublicMethods ().RequireThisNonPublicMethods (); + GetWithNonPublicMethods ().RequireThisNonPublicMethods (); + } + + [ExpectedWarning ("IL2075", + "Mono.Linker.Tests.Cases.DataFlow.MethodThisDataFlow.GetWithNonPublicMethods()", + "System.MethodThisDataFlowTypeTest.PropertyRequireThisPublicMethods.get")] + [ExpectedWarning ("IL2075", nameof (MethodThisDataFlowTypeTest.PropertyRequireThisNonPublicMethods) + ".get")] + static void PropagateToThisWithGetters () + { + _ = GetWithPublicMethods ().PropertyRequireThisPublicMethods; + _ = GetWithNonPublicMethods ().PropertyRequireThisPublicMethods; + + _ = GetWithPublicMethods ().PropertyRequireThisNonPublicMethods; + _ = GetWithNonPublicMethods ().PropertyRequireThisNonPublicMethods; + } + + [ExpectedWarning ("IL2075", + "Mono.Linker.Tests.Cases.DataFlow.MethodThisDataFlow.GetWithNonPublicMethods()", + "System.MethodThisDataFlowTypeTest.PropertyRequireThisPublicMethods.set")] + [ExpectedWarning ("IL2075", nameof (MethodThisDataFlowTypeTest.PropertyRequireThisNonPublicMethods) + ".set")] + static void PropagateToThisWithSetters () + { + GetWithPublicMethods ().PropertyRequireThisPublicMethods = null; + GetWithNonPublicMethods ().PropertyRequireThisPublicMethods = null; + GetWithPublicMethods ().PropertyRequireThisNonPublicMethods = null; + GetWithNonPublicMethods ().PropertyRequireThisNonPublicMethods = null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + static MethodThisDataFlowTypeTest GetWithPublicMethods () + { + return null; + } + + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + static MethodThisDataFlowTypeTest GetWithNonPublicMethods () + { + return null; + } + + static void AssignToThis () + { + var s = new StructType (); + s.AssignToThis (); + s.AssignToThisCaptured (); + } + + static void TestAnnotationOnNonTypeMethod () + { + var t = new NonTypeType (); + t.GetMethod ("foo"); + NonTypeType.StaticMethod (); + } + + [ExpectedWarning ("IL2065", nameof (MethodThisDataFlowTypeTest) + "." + nameof (MethodThisDataFlowTypeTest.RequireThisNonPublicMethods), "'this'", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void TestUnknownThis () + { + var array = new object[1]; + array[0] = array.GetType (); + MakeArrayValuesUnknown (array); + ((MethodThisDataFlowTypeTest) array[0]).RequireThisNonPublicMethods (); + + static void MakeArrayValuesUnknown (object[] array) + { + } + } + + [ExpectedWarning ("IL2070", "sourceType", nameof (TestFromParameterToThis), nameof (MethodThisDataFlowTypeTest.RequireThisPublicMethods))] + static void TestFromParameterToThis (MethodThisDataFlowTypeTest sourceType) + { + sourceType.RequireThisPublicMethods (); + } + + static MethodThisDataFlowTypeTest _typeField; + + [ExpectedWarning ("IL2080", nameof (_typeField), nameof (MethodThisDataFlowTypeTest.RequireThisPublicMethods))] + static void TestFromFieldToThis () + { + _typeField.RequireThisPublicMethods (); + } + + [ExpectedWarning ("IL2090", + "TSource", + "TestFromGenericParameterToThis", + nameof (MethodThisDataFlowTypeTest.RequireThisPublicMethods))] + static void TestFromGenericParameterToThis () + { + ((MethodThisDataFlowTypeTest) typeof (TSource)).RequireThisPublicMethods (); + } + + static void TestFromThisToOthers () + { + GetWithPublicMethods ().PropagateToReturn (); + GetWithPublicMethods ().PropagateToField (); + GetWithPublicMethods ().PropagateToThis (); + } + + class NonTypeType + { + [ExpectedWarning ("IL2041")] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public MethodInfo GetMethod (string name) + { + return null; + } + + [ExpectedWarning ("IL2041")] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public static void StaticMethod () + { + } + } + + struct StructType + { + int f; + public StructType (int f) => this.f = f; + + public void AssignToThis () + { + // Not relevant for dataflow, but this should not crash the analyzer. + this = new StructType (); + } + + public void AssignToThisCaptured () + { + // Not relevant for dataflow, but this should not crash the analyzer. + this = string.Empty.Length == 0 ? new StructType (1) : new StructType (2); + } + } + } +} + +namespace System +{ + class MethodThisDataFlowTypeTest : TestSystemTypeBase + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [ExpectedWarning ("IL2082", nameof (MethodThisDataFlowTypeTest) + "." + nameof (RequireNonPublicMethods) + "(Type)", + "'type' argument ", "in call to 'System.MethodThisDataFlowTypeTest.RequireNonPublicMethods(Type)'", + "implicit 'this' argument of method 'System.MethodThisDataFlowTypeTest.RequireThisPublicMethods()'")] + public void RequireThisPublicMethods () + { + RequirePublicMethods (this); + RequireNonPublicMethods (this); + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + [ExpectedWarning ("IL2082", nameof (MethodThisDataFlowTypeTest) + "." + nameof (RequirePublicMethods) + "(Type)")] + public void RequireThisNonPublicMethods () + { + RequirePublicMethods (this); + RequireNonPublicMethods (this); + } + + [ExpectedWarning ("IL2083", + nameof (PropagateToReturn), + nameof (PropagateToReturn))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + public Type PropagateToReturn () + { + return this; + } + + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] + Type _requiresPublicConstructors; + + [ExpectedWarning ("IL2084", nameof (MethodThisDataFlowTypeTest) + "." + nameof (_requiresPublicConstructors), + nameof (PropagateToField))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public void PropagateToField () + { + _requiresPublicConstructors = this; + } + + [ExpectedWarning ("IL2085", + nameof (PropagateToThis), + nameof (RequireThisNonPublicMethods))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public void PropagateToThis () + { + this.RequireThisNonPublicMethods (); + } + + public object PropertyRequireThisPublicMethods { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + get { + return null; + } + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + set { + return; + } + } + + public object PropertyRequireThisNonPublicMethods { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + get { + return null; + } + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] + set { + return; + } + } + + private static void RequirePublicMethods ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + Type type) + { + } + + private static void RequireNonPublicMethods ( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] + Type type) + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs new file mode 100644 index 0000000000000..2edf35569dedf --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/TypeBaseTypeDataFlow.cs @@ -0,0 +1,339 @@ +// 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.Reflection; +using System.Text; +using System.Threading.Tasks; +using Mono.Linker.Tests.Cases.Expectations.Assertions; +using Mono.Linker.Tests.Cases.Expectations.Helpers; +using Mono.Linker.Tests.Cases.Expectations.Metadata; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [SkipKeptItemsValidation] + [SandboxDependency ("Dependencies/TestSystemTypeBase.cs")] + [ExpectedNoWarnings] + public class TypeBaseTypeDataFlow + { + public static void Main () + { + TestAllPropagated (typeof (TestType)); + AllPropagatedWithDerivedClass.Test (); + + TestPublicConstructorsAreNotPropagated (typeof (TestType)); + TestPublicEventsPropagated (typeof (TestType)); + TestPublicFieldsPropagated (typeof (TestType)); + TestPublicMethodsPropagated (typeof (TestType)); + TestPublicNestedTypesAreNotPropagated (typeof (TestType)); + TestPublicParameterlessConstructorIsNotPropagated (typeof (TestType)); + TestPublicPropertiesPropagated (typeof (TestType)); + + TestNonPublicConstructorsAreNotPropagated (typeof (TestType)); + TestNonPublicEventsAreNotPropagated (typeof (TestType)); + TestNonPublicFieldsAreNotPropagated (typeof (TestType)); + TestNonPublicMethodsAreNotPropagated (typeof (TestType)); + TestNonPublicNestedTypesAreNotPropagated (typeof (TestType)); + TestNonPublicPropertiesAreNotPropagated (typeof (TestType)); + + TestInterfacesPropagated (typeof (TestType)); + + TestCombinationOfPublicsIsPropagated (typeof (TestType)); + TestCombinationOfNonPublicsIsNotPropagated (typeof (TestType)); + TestCombinationOfPublicAndNonPublicsPropagatesPublicOnly (typeof (TestType)); + + TestNoAnnotation (typeof (TestType)); + TestAnnotatedAndUnannotated (typeof (TestType), typeof (TestType), 0); + TestNull (); + TestNoValue (); + + Mixed_Derived.Test (typeof (TestType), 0); + + LoopPatterns.Test (); + } + + static void TestAllPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type derivedType) + { + derivedType.BaseType.RequiresAll (); + } + + class AllPropagatedWithDerivedClass + { + // https://github.com/dotnet/linker/issues/2673 + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions.RequiresAll) + "(Type)", nameof (TestSystemTypeBase.BaseType) + ".get", + ProducedBy = ProducedBy.Analyzer)] + static void TestAllPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] TestSystemTypeBase derivedType) + { + derivedType.BaseType.RequiresAll (); + } + + // This is a very special case - normally there's basically no way to "new up" a Type instance via the "new" operator. + // The linker and analyzer see an unknown value and thus warns that it doesn't fulfill the All annotation. + [ExpectedWarning ("IL2062", nameof (TestAllPropagated))] + public static void Test () + { + TestAllPropagated (new TestSystemTypeBase ()); + } + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicConstructors))] + static void TestPublicConstructorsAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors)] Type derivedType) + { + derivedType.BaseType.RequiresPublicConstructors (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicEvents))] + static void TestPublicEventsPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicEvents)] Type derivedType) + { + derivedType.BaseType.RequiresPublicEvents (); + + // Should warn + derivedType.BaseType.RequiresPublicMethods (); + + // Should warn + derivedType.BaseType.RequiresNonPublicEvents (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicFields))] + static void TestPublicFieldsPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicFields)] Type derivedType) + { + derivedType.BaseType.RequiresPublicFields (); + + // Should warn + derivedType.BaseType.RequiresPublicMethods (); + + // Should warn + derivedType.BaseType.RequiresNonPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicProperties))] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))] + static void TestPublicMethodsPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type derivedType) + { + derivedType.BaseType.RequiresPublicMethods (); + + // Should warn + derivedType.BaseType.RequiresPublicProperties (); + + // Should warn + derivedType.BaseType.RequiresNonPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicNestedTypes))] + static void TestPublicNestedTypesAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicNestedTypes)] Type derivedType) + { + derivedType.BaseType.RequiresPublicNestedTypes (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicParameterlessConstructor))] + static void TestPublicParameterlessConstructorIsNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] Type derivedType) + { + derivedType.BaseType.RequiresPublicParameterlessConstructor (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicProperties))] + static void TestPublicPropertiesPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicProperties)] Type derivedType) + { + derivedType.BaseType.RequiresPublicProperties (); + + // Should warn + derivedType.BaseType.RequiresPublicMethods (); + + // Should warn + derivedType.BaseType.RequiresNonPublicProperties (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicConstructors))] + static void TestNonPublicConstructorsAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicConstructors (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicEvents))] + static void TestNonPublicEventsAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicEvents)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicEvents (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicFields))] + static void TestNonPublicFieldsAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicFields)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicFields (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))] + static void TestNonPublicMethodsAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicNestedTypes))] + static void TestNonPublicNestedTypesAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicNestedTypes)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicNestedTypes (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicProperties))] + static void TestNonPublicPropertiesAreNotPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicProperties)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicProperties (); + } + + static void TestInterfacesPropagated ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type derivedType) + { + derivedType.BaseType.RequiresInterfaces (); + } + + static void TestCombinationOfPublicsIsPropagated ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)] Type derivedType) + { + derivedType.BaseType.RequiresPublicMethods (); + derivedType.BaseType.RequiresPublicProperties (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))] + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicProperties))] + static void TestCombinationOfNonPublicsIsNotPropagated ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicMethods (); + derivedType.BaseType.RequiresNonPublicProperties (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresNonPublicMethods))] + static void TestCombinationOfPublicAndNonPublicsPropagatesPublicOnly ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.NonPublicMethods | DynamicallyAccessedMemberTypes.PublicProperties)] Type derivedType) + { + derivedType.BaseType.RequiresNonPublicMethods (); + derivedType.BaseType.RequiresPublicProperties (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestNoAnnotation (Type derivedType) + { + derivedType.BaseType.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072", nameof (DataFlowTypeExtensions) + "." + nameof (DataFlowTypeExtensions.RequiresPublicMethods))] + static void TestAnnotatedAndUnannotated ( + Type derivedTypeOne, + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type derivedTypeTwo, + int number) + { + Type type = number > 0 ? derivedTypeOne : derivedTypeTwo; + type.BaseType.RequiresPublicMethods (); + } + + static void TestNull () + { + Type type = null; + type.BaseType.RequiresPublicMethods (); + } + + static void TestNoValue () + { + Type t = null; + Type noValue = Type.GetTypeFromHandle (t.TypeHandle); + // No warning because the above throws an exception at runtime. + noValue.BaseType.RequiresPublicMethods (); + } + + class Mixed_Base + { + public static void PublicMethod () { } + private static void PrivateMethod () { } + } + + class Mixed_Derived : Mixed_Base + { + public static void Test ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type typeWithPublicMethods, + int number) + { + Type type; + switch (number) { + case 0: + type = typeof (TestType); + break; + case 1: + type = typeof (Mixed_Derived); + break; + case 2: + type = typeWithPublicMethods; + break; + default: + type = null; + break; + } + + type.BaseType.RequiresPublicMethods (); + } + } + + class LoopPatterns + { + static void EnumerateInterfacesOnBaseTypes ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.Interfaces)] Type type) + { + Type? t = type; + while (t != null) { + Type[] interfaces = t.GetInterfaces (); + t = t.BaseType; + } + } + + [ExpectedWarning ("IL2070")] + [ExpectedWarning ("IL2075", ProducedBy = ProducedBy.Analyzer)] // Linker doesn't implement backward branches data flow yet + static void EnumerateInterfacesOnBaseTypes_Unannotated (Type type) + { + Type? t = type; + while (t != null) { + Type[] interfaces = t.GetInterfaces (); + t = t.BaseType; + } + } + + // Can only work with All annotation as NonPublicProperties doesn't propagate to base types + static void EnumeratePrivatePropertiesOnBaseTypes ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + Type? t = type; + while (t != null) { + t.GetProperties (DeclaredOnlyLookup).GetEnumerator (); + t = t.BaseType; + } + } + + // Can only work with All annotation as NonPublicProperties doesn't propagate to base types + static void EnumeratePrivatePropertiesOnBaseTypesWithForeach ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) + { + const BindingFlags DeclaredOnlyLookup = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly; + Type? t = type; + while (t != null) { + foreach (var p in t.GetProperties (DeclaredOnlyLookup)) { + // Do nothing + } + t = t.BaseType; + } + } + + public static void Test () + { + EnumerateInterfacesOnBaseTypes (typeof (TestType)); + EnumerateInterfacesOnBaseTypes_Unannotated (typeof (TestType)); + + EnumeratePrivatePropertiesOnBaseTypes (typeof (TestType)); + EnumeratePrivatePropertiesOnBaseTypesWithForeach (typeof (TestType)); + } + } + + class TestType + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/UnresolvedMembers.cs b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/UnresolvedMembers.cs new file mode 100644 index 0000000000000..400dd9af052bd --- /dev/null +++ b/src/coreclr/tools/aot/Mono.Linker.Tests.Cases/DataFlow/UnresolvedMembers.cs @@ -0,0 +1,135 @@ +// 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.DataFlow +{ + // NativeAOT will not compile a method with unresolved types in it - it will instead replace it with a throwing method body + // So it doesn't produce any of these warnings - which is also correct, because the code at runtime would never get there + // it would fail to JIT/run anyway. + + [SkipPeVerify] + [SetupLinkerArgument ("--skip-unresolved", "true")] + [SetupCompileBefore ("UnresolvedLibrary.dll", new[] { "Dependencies/UnresolvedLibrary.cs" }, removeFromLinkerInput: true)] + [ExpectedNoWarnings] + class UnresolvedMembers + { + public static void Main () + { + UnresolvedGenericArgument (); + UnresolvedAttributeArgument (); + UnresolvedAttributePropertyValue (); + UnresolvedAttributeFieldValue (); + UnresolvedObjectGetType (); + UnresolvedMethodParameter (); + } + + [Kept] + [KeptMember (".ctor()")] + class TypeWithUnresolvedGenericArgument< + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> + { + } + + [Kept] + static void MethodWithUnresolvedGenericArgument< + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] T> () + { } + + [Kept] + [ExpectedWarning ("IL2066", "TypeWithUnresolvedGenericArgument", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // Local variable type + [ExpectedWarning ("IL2066", "TypeWithUnresolvedGenericArgument", ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] // Called method declaring type + [ExpectedWarning ("IL2066", nameof (MethodWithUnresolvedGenericArgument), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + static void UnresolvedGenericArgument () + { + var a = new TypeWithUnresolvedGenericArgument (); + MethodWithUnresolvedGenericArgument (); + } + + [Kept] + [KeptBaseType (typeof (Attribute))] + class AttributeWithRequirements : Attribute + { + [Kept] + public AttributeWithRequirements ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { } + + [Kept] + [KeptBackingField] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type PropertyWithRequirements { get; [Kept] set; } + + [Kept] + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type FieldWithRequirements; + } + + [Kept] + [ExpectedWarning ("IL2062", nameof (AttributeWithRequirements), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [KeptAttributeAttribute (typeof (AttributeWithRequirements))] + [AttributeWithRequirements (typeof (Dependencies.UnresolvedType))] + static void UnresolvedAttributeArgument () + { + } + + [Kept] + [ExpectedWarning ("IL2062", nameof (AttributeWithRequirements.PropertyWithRequirements), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [KeptAttributeAttribute (typeof (AttributeWithRequirements))] + [AttributeWithRequirements (typeof (EmptyType), PropertyWithRequirements = typeof (Dependencies.UnresolvedType))] + static void UnresolvedAttributePropertyValue () + { + } + + [Kept] + [ExpectedWarning ("IL2064", nameof (AttributeWithRequirements.FieldWithRequirements), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + [KeptAttributeAttribute (typeof (AttributeWithRequirements))] + [AttributeWithRequirements (typeof (EmptyType), FieldWithRequirements = typeof (Dependencies.UnresolvedType))] + static void UnresolvedAttributeFieldValue () + { + } + + [Kept] + static Dependencies.UnresolvedType _unresolvedField; + + [Kept] + [ExpectedWarning ("IL2072", nameof (Object.GetType), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + static void UnresolvedObjectGetType () + { + RequirePublicMethods (_unresolvedField.GetType ()); + } + + [Kept] + [ExpectedWarning ("IL2072", nameof (Object.GetType), ProducedBy = ProducedBy.Trimmer | ProducedBy.Analyzer)] + static void UnresolvedMethodParameter () + { + RequirePublicMethods (typeof (Dependencies.UnresolvedType)); + } + + [Kept] + class EmptyType + { + } + + [Kept] + static void RequirePublicMethods ( + [KeptAttributeAttribute (typeof (DynamicallyAccessedMembersAttribute))] + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type t) + { + } + } +} diff --git a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs index cd3a1c2eca251..a6adaf187d4c5 100644 --- a/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs +++ b/src/coreclr/tools/aot/Mono.Linker.Tests/TestCasesRunner/ResultChecker.cs @@ -109,7 +109,7 @@ void VerifyLoggedMessages (AssemblyDefinition original, TestLogWriter logger, bo List loggedMessages = logger.GetLoggedMessages (); List<(IMemberDefinition, CustomAttribute)> expectedNoWarningsAttributes = new List<(IMemberDefinition, CustomAttribute)> (); foreach (var attrProvider in GetAttributeProviders (original)) { - if (attrProvider.ToString() is String mystring && mystring.Contains ("RequiresInCompilerGeneratedCode/SuppressInLambda")) + if (attrProvider.ToString () is String mystring && mystring.Contains ("RequiresInCompilerGeneratedCode/SuppressInLambda")) Debug.WriteLine ("Print"); foreach (var attr in attrProvider.CustomAttributes) { if (!IsProducedByNativeAOT (attr)) @@ -220,7 +220,7 @@ void VerifyLoggedMessages (AssemblyDefinition original, TestLogWriter logger, bo if (attrProvider is not IMemberDefinition expectedMember) continue; - string actualName = methodDesc.OwningType.ToString ().Replace("+", ".") + "." + methodDesc.Name; + string actualName = methodDesc.OwningType.ToString ().Replace ("+", ".") + "." + methodDesc.Name; if (actualName.Contains (expectedMember.DeclaringType.FullName.Replace ("/", ".")) && actualName.Contains ("<" + expectedMember.Name + ">")) { expectedWarningFound = true; @@ -386,7 +386,29 @@ static string ConvertSignatureToIlcFormat (string value) value = value.Replace (", ", ","); } - return value; + // Split it into . separated parts and if one is ending with > rewrite it to `1 format + // ILC folows the reflection format which doesn't actually use generic instantiations on anything but the last type + // in nested hierarchy - it's difficult to replicate this with Cecil as it has different representation so just strip that info + var parts = value.Split ('.'); + StringBuilder sb = new StringBuilder (); + foreach (var part in parts) { + if (sb.Length > 0) + sb.Append ("."); + + if (part.EndsWith ('>')) { + int i = part.LastIndexOf ('<'); + if (i >= 0) { + sb.Append (part.Substring (0, i)); + sb.Append ("`"); + sb.Append (part.Substring (i + 1).Where (c => c == ',').Count () + 1); + continue; + } + } + + sb.Append (part); + } + + return sb.ToString (); } }