diff --git a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs index 1c5f10340ae4e1..ad20063a06a7c2 100644 --- a/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs +++ b/src/tools/illink/src/linker/Linker.Steps/MarkStep.cs @@ -1793,6 +1793,7 @@ void ProcessAnalysisAnnotationsForField (FieldDefinition field, DependencyKind d case DependencyKind.DynamicDependency: case DependencyKind.DynamicallyAccessedMember: case DependencyKind.InteropMethodDependency: + case DependencyKind.Ldtoken: if (isReflectionAccessCoveredByDAM = Annotations.FlowAnnotations.ShouldWarnWhenAccessedForReflection (field)) Context.LogWarning (origin, DiagnosticId.DynamicallyAccessedMembersFieldAccessedViaReflection, field.GetDisplayName ()); diff --git a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs index e622c02e02eb25..d589a6f3a0457c 100644 --- a/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs +++ b/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests/DataFlowTests.cs @@ -100,6 +100,12 @@ public Task CompilerGeneratedCodeAccessedViaReflection () return RunTest (); } + [Fact] + public Task ConstructedTypesDataFlow () + { + return RunTest (); + } + [Fact] public Task DynamicDependencyDataflow () { diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs index 288c59c27e1361..1a00f9325cf3bc 100644 --- a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/AnnotatedMembersAccessedViaReflection.cs @@ -32,7 +32,6 @@ public static void Main () AnnotatedGenerics.Test (); AnnotationOnGenerics.Test (); AnnotationOnInteropMethod.Test (); - AccessThroughLdToken.Test (); } class AnnotatedField @@ -128,6 +127,17 @@ static void DynamicallyAccessedMembersNestedTypes2 () typeof (AnnotatedField).RequiresNonPublicNestedTypes (); } + static void PotentialWriteAccess (ref Type type) + { + } + + // https://github.com/dotnet/linker/issues/3172 + [ExpectedWarning ("IL2110", nameof (AnnotatedField._annotatedField), ProducedBy = ProducedBy.Trimmer)] + static void LdToken () + { + Expression a = () => PotentialWriteAccess (ref _annotatedField); + } + [UnconditionalSuppressMessage ("test", "IL2026")] public static void Test () { @@ -143,6 +153,7 @@ public static void Test () DynamicallyAccessedMembersAll2 (); DynamicallyAccessedMembersNestedTypes1 (); DynamicallyAccessedMembersNestedTypes2 (); + LdToken (); } } @@ -251,6 +262,14 @@ static void DynamicallyAccessedMembersAll2 () typeof (AnnotatedMethodParameters).RequiresAll (); } + // https://github.com/dotnet/linker/issues/3172 + [ExpectedWarning ("IL2111", nameof (MethodWithSingleAnnotatedParameter), ProducedBy = ProducedBy.Trimmer)] + [ExpectedWarning ("IL2067", nameof (MethodWithSingleAnnotatedParameter), ProducedBy = ProducedBy.Analyzer)] + static void LdToken () + { + Expression> _ = (Type t) => MethodWithSingleAnnotatedParameter (t); + } + [UnconditionalSuppressMessage ("test", "IL2026")] public static void Test () { @@ -265,6 +284,7 @@ public static void Test () Ldvirtftn (); DynamicallyAccessedMembersAll1 (); DynamicallyAccessedMembersAll2 (); + LdToken (); } } @@ -363,6 +383,18 @@ static void LdftnOnVirtual () var _ = new Func ((new AnnotatedMethodReturnValue ()).VirtualMethodWithAnnotatedReturnValue); } + static void LdTokenOnStatic () + { + Expression _ = () => StaticMethodWithAnnotatedReturnValue (); + } + + // https://github.com/dotnet/linker/issues/3172 + [ExpectedWarning ("IL2111", nameof (VirtualMethodWithAnnotatedReturnValue), ProducedBy = ProducedBy.Trimmer)] + static void LdTokenOnVirtual () + { + Expression> _ = (a) => a.VirtualMethodWithAnnotatedReturnValue (); + } + [UnconditionalSuppressMessage ("test", "IL2026")] public static void Test () { @@ -380,6 +412,8 @@ public static void Test () LdftnOnStatic (); LdftnOnInstance (); LdftnOnVirtual (); + LdTokenOnStatic (); + LdTokenOnVirtual (); } } @@ -563,6 +597,13 @@ static void DynamicallyAccessedFields () typeof (AnnotatedProperty).RequiresNonPublicFields (); } + // Action delegate is not handled correctly https://github.com/dotnet/linker/issues/2561 + [ExpectedWarning ("IL2111", nameof (Property1WithAnnotation), ProducedBy = ProducedBy.Trimmer)] + static void LdToken () + { + Expression> _ = () => Property1WithAnnotation; + } + [UnconditionalSuppressMessage ("test", "IL2026")] public static void Test () { @@ -581,6 +622,7 @@ public static void Test () DynamicallyAccessedMembersAll1 (); DynamicallyAccessedMembersAll2 (); DynamicallyAccessedFields (); + LdToken (); } } @@ -623,6 +665,12 @@ static void DynamicallyAccessedMembersAll () typeof (AnnotatedGenerics).RequiresAll (); } + [ExpectedWarning ("IL2091", nameof (GenericWithAnnotation))] + static void LdToken () + { + Expression _ = () => GenericWithAnnotation (); + } + public static void Test () { ReflectionOnly (); @@ -630,6 +678,7 @@ public static void Test () DynamicallyAccessedMembers (); InstantiateGeneric (); DynamicallyAccessedMembersAll (); + LdToken (); } } @@ -693,6 +742,17 @@ static void DynamicallyAccessedMembersAll2 () typeof (AnnotationOnGenerics).RequiresAll (); } + // https://github.com/dotnet/linker/issues/3172 + [ExpectedWarning ("IL2111", "GenericWithAnnotatedMethod", "AnnotatedMethod", ProducedBy = ProducedBy.Trimmer)] + static void LdToken () + { + // Note that this should warn even though the code looks "Correct" + // That is because under the hood the expression tree create MethodInfo which is accessible by anything + // which gets the expression tree as input (so some queryable) and that could invoke the method + // with a different parameter value and thus violate the requirements. + Expression _ = () => GenericWithAnnotatedMethod.AnnotatedMethod (typeof (TestType)); + } + public static void Test () { GenericTypeWithStaticMethodViaLdftn (); @@ -702,6 +762,7 @@ public static void Test () GenericMethodDynamicallyAccessedMembers (); DynamicallyAccessedMembersAll1 (); DynamicallyAccessedMembersAll2 (); + LdToken (); } } @@ -713,12 +774,12 @@ struct ValueWithAnnotatedField public Type _typeField; } - // Analyzer doesnt take into account interop attributes https://github.com/dotnet/linker/issues/2562 + // Analyzer doesn't take into account interop attributes https://github.com/dotnet/linker/issues/2562 [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField), ProducedBy = ProducedBy.Trimmer)] [DllImport ("nonexistent")] static extern ValueWithAnnotatedField GetValueWithAnnotatedField (); - // Analyzer doesnt take into account interop attributes https://github.com/dotnet/linker/issues/2562 + // Analyzer doesn't take into account interop attributes https://github.com/dotnet/linker/issues/2562 [ExpectedWarning ("IL2110", nameof (ValueWithAnnotatedField._typeField), ProducedBy = ProducedBy.Trimmer)] [DllImport ("nonexistent")] static extern void AcceptValueWithAnnotatedField (ValueWithAnnotatedField value); @@ -730,24 +791,6 @@ public static void Test () } } - class AccessThroughLdToken - { - public virtual Type PropertyWithLdToken { - [return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] - get { - return null; - } - } - - // Action delegate is not handled correctly https://github.com/dotnet/linker/issues/2561 - [ExpectedWarning ("IL2111", nameof (PropertyWithLdToken), ProducedBy = ProducedBy.Trimmer)] - [ExpectedWarning ("IL2111", nameof (PropertyWithLdToken), ProducedBy = ProducedBy.Trimmer)] - public static void Test () - { - Expression> getter = () => (new AccessThroughLdToken ()).PropertyWithLdToken; - } - } - public class AnnotatedAttributeConstructorAttribute : Attribute { public AnnotatedAttributeConstructorAttribute ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)] Type type) diff --git a/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructedTypesDataFlow.cs b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructedTypesDataFlow.cs new file mode 100644 index 00000000000000..ee085aafaee926 --- /dev/null +++ b/src/tools/illink/test/Mono.Linker.Tests.Cases/DataFlow/ConstructedTypesDataFlow.cs @@ -0,0 +1,194 @@ +// 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.Helpers; + +namespace Mono.Linker.Tests.Cases.DataFlow +{ + [ExpectedNoWarnings] + [SkipKeptItemsValidation] + class ConstructedTypesDataFlow + { + public static void Main () + { + DeconstructedVariable.Test (); + ConstructedVariable.Test (); + } + + class DeconstructedVariable + { + // https://github.com/dotnet/linker/issues/3158 + [ExpectedWarning ("IL2077", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void DeconstructVariableNoAnnotation ((Type type, object instance) input) + { + var (type, instance) = input; + type.RequiresPublicMethods (); + } + + record TypeAndInstance ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [property: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type type, + object instance); + + // In IL based tools this is a behavior of the compiler. The attribute on the record declaration parameter + // is only propagated to the .ctor constructor parameter. The property and field attributes are applied to the + // generated property and field respectively. But none of the attributes is propagated to the Deconstruct method parameters. + // For analyzer, this is currently + // https://github.com/dotnet/linker/issues/3158 + // But it's possible that with that fixed there won't be a warning from the analyzer anyway (depends on the implementation) + [ExpectedWarning ("IL2067", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void DeconstructRecordWithAnnotation (TypeAndInstance value) + { + var (type, instance) = value; + type.RequiresPublicMethods (); + } + + class TypeAndInstanceManual + { + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + public Type type; + public object instance; + + public TypeAndInstanceManual ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type, object instance) + => (this.type, this.instance) = (type, instance); + + public void Deconstruct ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] out Type type, out object instance) + => (type, instance) = (this.type, this.instance); + } + + // This case actually works because the annotation is correctly propagated through the Deconstruct + static void DeconstructClassWithAnnotation (TypeAndInstanceManual value) + { + var (type, instance) = value; + type.RequiresPublicMethods (); + } + + record TypeAndInstanceRecordManual ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + [property: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] + Type type, + object instance) + { + // The generated property getter doesn't have the same attributes??? + // The attributes are only propagated to the generated .ctor - so suppressing the warning the this.type doesn't have the matching annotations + //[UnconditionalSuppressMessage ("", "IL2072")] + public void Deconstruct ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] out Type type, out object instance) + => (type, instance) = (this.type, this.instance); + } + + static void DeconstructRecordManualWithAnnotation (TypeAndInstanceRecordManual value) + { + var (type, instance) = value; + type.RequiresPublicMethods (); + } + + // https://github.com/dotnet/linker/issues/3158 + [ExpectedWarning ("IL2067", ProducedBy = ProducedBy.Trimmer | ProducedBy.NativeAot)] + static void DeconstructRecordManualWithMismatchAnnotation (TypeAndInstanceRecordManual value) + { + var (type, instance) = value; + type.RequiresPublicFields (); + } + + public static void Test () + { + DeconstructVariableNoAnnotation ((typeof (string), null)); + DeconstructRecordWithAnnotation (new (typeof (string), null)); + DeconstructClassWithAnnotation (new (typeof (string), null)); + DeconstructRecordManualWithAnnotation (new (typeof (string), null)); + DeconstructRecordManualWithMismatchAnnotation (new (typeof (string), null)); + } + } + + class ConstructedVariable + { + [ExpectedWarning ("IL2077")] + static void ConstructedType () + { + var ct = (typeof (string), 1); + ct.Item1.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2077")] + static void ConstructedTypeNamed () + { + (Type Type, int Value) ct = (typeof (string), 1); + ct.Type.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2077")] + static void ConstructedTypeWithAnnotations ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { + var ct = (type, 1); + ct.Item1.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072")] + static void AnonymousTypeWithoutAnnotations () + { + var ct = new { + Type = typeof (string), + Value = 1 + }; + + ct.Type.RequiresPublicMethods (); + } + + [ExpectedWarning ("IL2072")] + static void AnonymousTypeWithExplicitTypesWithoutAnnotations () + { + var ct = new { + Type = typeof (string), + Value = 1 + }; + + ct.Type.RequiresPublicMethods (); + } + + // Compiler doesn't propagate attributes, only types + [ExpectedWarning ("IL2072")] + static void AnonymousTypeWithAnnotation ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type type) + { + var ct = new { + Type = type, + Value = 1 + }; + + ct.Type.RequiresPublicMethods (); + } + + record TypeAndValue ([DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type Type, int Value); + + [ExpectedWarning ("IL2067", "typeUnknown")] + static void RecordConstruction ( + [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicMethods)] Type typeWithPublicMethods, + Type typeUnknown) + { + _ = new TypeAndValue (typeof (string), 1); + _ = new TypeAndValue (typeWithPublicMethods, 2); + _ = new TypeAndValue (typeUnknown, 3); + } + + public static void Test () + { + ConstructedType (); + ConstructedTypeNamed (); + ConstructedTypeWithAnnotations (typeof (string)); + + AnonymousTypeWithoutAnnotations (); + AnonymousTypeWithExplicitTypesWithoutAnnotations (); + AnonymousTypeWithAnnotation (typeof (string)); + + RecordConstruction (typeof (string), typeof (string)); + } + } + } +}