From 9c731eac23b8b8e0e10b275b1b679f4716aeabe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Tue, 30 Jan 2024 22:33:47 +0100 Subject: [PATCH] WIP: More trimming annotations in DotNext.Metaprogramming. --- .../Expressions/CollectionAccessExpression.cs | 3 +- .../Linq/Expressions/ForEachExpression.cs | 4 +-- .../Linq/Expressions/MetaExpression.cs | 6 ++-- .../Metaprogramming/AsyncLambdaExpression.cs | 6 ++-- .../Metaprogramming/AwaitForEachStatement.cs | 4 ++- .../Metaprogramming/CodeGenerator.cs | 29 +++++++++++++------ .../Metaprogramming/ForEachStatement.cs | 6 ++-- .../Metaprogramming/LambdaExpression.cs | 5 ++-- .../Metaprogramming/MatchBuilder.cs | 16 +++++++++- .../Runtime/CompilerServices/TaskType.cs | 2 +- 10 files changed, 58 insertions(+), 23 deletions(-) diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs index 86f4787a3a..e375307d8c 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/CollectionAccessExpression.cs @@ -1,4 +1,5 @@ using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; @@ -156,4 +157,4 @@ protected override CollectionAccessExpression VisitChildren(ExpressionVisitor vi var collection = visitor.Visit(Collection); return ReferenceEquals(index, Index) && ReferenceEquals(collection, Collection) ? this : new(collection, index); } -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs index 810a761a2e..9192622213 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/ForEachExpression.cs @@ -14,7 +14,7 @@ namespace DotNext.Linq.Expressions; /// Represents iteration over collection elements as expression. /// /// foreach Statement -[RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] +[RequiresUnreferencedCode("Dynamic access to GetEnumerator (GetAsyncEnumerator) method and IEnumerable (IAsyncEnumerable) interfaces.")] public sealed class ForEachExpression : CustomExpression, ILoopLabels { private const string EnumeratorVarName = "enumerator"; @@ -224,4 +224,4 @@ public override Expression Reduce() return Reduce(moveNextCall, disposeCall); } -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs index 37b2245a30..cc8a5633b3 100644 --- a/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs +++ b/src/DotNext.Metaprogramming/Linq/Expressions/MetaExpression.cs @@ -1,4 +1,5 @@ -using System.Dynamic; +using System.Diagnostics.CodeAnalysis; +using System.Dynamic; using System.Linq.Expressions; using System.Reflection; using Debug = System.Diagnostics.Debug; @@ -8,6 +9,7 @@ namespace DotNext.Linq.Expressions; using Intrinsics = Runtime.Intrinsics; +[RequiresUnreferencedCode("Binds to arbitrary members.")] internal sealed class MetaExpression : DynamicMetaObject { private static readonly MethodInfo AsExpressionBuilderMethod = new Func?>(Unsafe.As>).Method; @@ -178,4 +180,4 @@ public override DynamicMetaObject BindCreateInstance(CreateInstanceBinder binder return new MetaExpression(binding, CreateRestrictions().Merge(restrictions)); } -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs b/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs index 7c0b3ecc1f..3f6a630252 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/AsyncLambdaExpression.cs @@ -2,12 +2,14 @@ namespace DotNext.Metaprogramming; +using System.Diagnostics.CodeAnalysis; using Linq.Expressions; using Runtime.CompilerServices; using static Reflection.DelegateType; using List = Collections.Generic.List; -internal sealed class AsyncLambdaExpression : LambdaExpression, ILexicalScope, Action>, ILexicalScope, Action> +[RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] +internal sealed class AsyncLambdaExpression<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate> : LambdaExpression, ILexicalScope, Action>, ILexicalScope, Action> where TDelegate : Delegate { private readonly bool usePooling; @@ -99,4 +101,4 @@ public override void Dispose() recursion = null; base.Dispose(); } -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Metaprogramming/AwaitForEachStatement.cs b/src/DotNext.Metaprogramming/Metaprogramming/AwaitForEachStatement.cs index 6e62b89194..dbc2303069 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/AwaitForEachStatement.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/AwaitForEachStatement.cs @@ -1,9 +1,11 @@ +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; namespace DotNext.Metaprogramming; using ForEachExpression = Linq.Expressions.ForEachExpression; +[RequiresUnreferencedCode("Dynamic access to GetAsyncEnumerator method and IAsyncEnumerable interfaces.")] internal sealed class AwaitForEachStatement : LoopLexicalScope, ILexicalScope>, ILexicalScope> { private readonly Expression collection; @@ -33,4 +35,4 @@ ForEachExpression ILexicalScopeThe method to be called. /// Method call arguments. /// Attempts to call this method out of lexical scope. + [RequiresUnreferencedCode("Dynamically accesses method .")] public static void Call(Expression instance, string methodName, params Expression[] arguments) => Statement(instance.Call(methodName, arguments)); @@ -378,6 +379,7 @@ public static void Call(Expression instance, string methodName, params Expressio /// The method to be called. /// Method call arguments. /// Attempts to call this method out of lexical scope. + [RequiresUnreferencedCode("Dynamically accesses method .")] public static void NullSafeCall(Expression instance, string methodName, params Expression[] arguments) => Statement(instance.IfNotNull(target => target.Call(methodName, arguments))); @@ -406,7 +408,8 @@ public static void CallStatic(MethodInfo method, params Expression[] arguments) /// The name of the static method. /// The arguments to be passed into static method. /// Attempts to call this method out of lexical scope. - public static void CallStatic(Type type, string methodName, params Expression[] arguments) + public static void CallStatic( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type type, string methodName, params Expression[] arguments) => Statement(type.CallStatic(methodName, arguments)); /// @@ -416,7 +419,7 @@ public static void CallStatic(Type type, string methodName, params Expression[] /// The name of the static method. /// The arguments to be passed into static method. /// Attempts to call this method out of lexical scope. - public static void CallStatic(string methodName, params Expression[] arguments) + public static void CallStatic<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] T>(string methodName, params Expression[] arguments) => CallStatic(typeof(T), methodName, arguments); /// @@ -609,6 +612,7 @@ public static void DoWhile(Expression test, Action body) /// Loop body. /// Attempts to call this method out of lexical scope. /// foreach Statement + [RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] public static void ForEach(Expression collection, Action body) { using var statement = new ForEachStatement(collection); @@ -622,6 +626,7 @@ public static void ForEach(Expression collection, ActionLoop body. /// Attempts to call this method out of lexical scope. /// foreach Statement + [RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] public static void ForEach(Expression collection, Action body) { using var statement = new ForEachStatement(collection); @@ -637,6 +642,7 @@ public static void ForEach(Expression collection, Action body) /// to call with argument when awaiting method. /// Attempts to call this method out of lexical scope. /// Async Streams + [RequiresUnreferencedCode("Dynamic access to GetAsyncEnumerator method and IAsyncEnumerable interfaces.")] public static void AwaitForEach(Expression collection, Action body, Expression? cancellationToken = null, bool configureAwait = false) { using var statement = new AwaitForEachStatement(collection, cancellationToken, configureAwait); @@ -652,6 +658,7 @@ public static void AwaitForEach(Expression collection, Action to call with argument when awaiting method. /// Attempts to call this method out of lexical scope. /// Async Streams + [RequiresUnreferencedCode("Dynamic access to GetAsyncEnumerator method and IAsyncEnumerable interfaces.")] public static void AwaitForEach(Expression collection, Action body, Expression? cancellationToken = null, bool configureAwait = false) { using var statement = new AwaitForEachStatement(collection, cancellationToken, configureAwait); @@ -934,7 +941,7 @@ public static void Return(Expression? result = null) /// if the lambda expression will be compiled with the tail call optimization, otherwise . /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(bool tailCall, Action body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(bool tailCall, Action body) where TDelegate : Delegate { using var expression = new LambdaExpression(tailCall); @@ -948,7 +955,7 @@ public static Expression Lambda(bool tailCall, Action if the lambda expression will be compiled with the tail call optimization, otherwise . /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(bool tailCall, Func body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(bool tailCall, Func body) where TDelegate : Delegate { using var expression = new LambdaExpression(tailCall); @@ -961,7 +968,7 @@ public static Expression Lambda(bool tailCall, FuncThe delegate describing signature of lambda function. /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(Func body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(Func body) where TDelegate : Delegate => Lambda(false, body); @@ -972,7 +979,7 @@ public static Expression Lambda(Func if the lambda expression will be compiled with the tail call optimization, otherwise . /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(bool tailCall, Action body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(bool tailCall, Action body) where TDelegate : Delegate { using var expression = new LambdaExpression(tailCall); @@ -985,7 +992,7 @@ public static Expression Lambda(bool tailCall, ActionThe delegate describing signature of lambda function. /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(Action body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(Action body) where TDelegate : Delegate => Lambda(false, body); @@ -995,7 +1002,7 @@ public static Expression Lambda(Action body /// The delegate describing signature of lambda function. /// Lambda function builder. /// Constructed lambda expression. - public static Expression Lambda(Action body) + public static Expression Lambda<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate>(Action body) where TDelegate : Delegate => Lambda(false, body); @@ -1008,6 +1015,7 @@ public static Expression Lambda(Action /// /// Async methods + [RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] public static Expression AsyncLambda(Action body) where TDelegate : Delegate => AsyncLambda(false, body); @@ -1025,6 +1033,7 @@ public static Expression AsyncLambda(Action /// /// /// Async methods + [RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] public static Expression AsyncLambda(bool usePooling, Action body) where TDelegate : Delegate => new AsyncLambdaExpression(usePooling).Build(body); @@ -1038,6 +1047,7 @@ public static Expression AsyncLambda(bool usePooling, Acti /// /// /// Async methods + [RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] public static Expression AsyncLambda(Action body) where TDelegate : Delegate => AsyncLambda(false, body); @@ -1055,6 +1065,7 @@ public static Expression AsyncLambda(Action /// /// Async methods + [RequiresUnreferencedCode("Dynamic access to Transition and IAsyncStateMachine internal types.")] public static Expression AsyncLambda(bool usePooling, Action body) where TDelegate : Delegate => new AsyncLambdaExpression(usePooling).Build(body); @@ -1104,4 +1115,4 @@ public static LambdaExpressionTree AsyncLambda(Type[] parameters, Type returnTyp /// The expression to add. public static void Statement(Expression expr) => LexicalScope.Current.AddStatement(expr); -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Metaprogramming/ForEachStatement.cs b/src/DotNext.Metaprogramming/Metaprogramming/ForEachStatement.cs index 856e1657b1..753db4b808 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/ForEachStatement.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/ForEachStatement.cs @@ -1,9 +1,11 @@ -using System.Linq.Expressions; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; namespace DotNext.Metaprogramming; using ForEachExpression = Linq.Expressions.ForEachExpression; +[RequiresUnreferencedCode("Dynamic access to GetEnumerator method and IEnumerable interfaces.")] internal sealed class ForEachStatement : LoopLexicalScope, ILexicalScope>, ILexicalScope> { private readonly Expression collection; @@ -26,4 +28,4 @@ ForEachExpression ILexicalScope /// The delegate describing signature of lambda function. -internal sealed class LambdaExpression : LambdaExpression, ILexicalScope, Action>, ILexicalScope, Action>, ILexicalScope, Func> +internal sealed class LambdaExpression<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TDelegate> : LambdaExpression, ILexicalScope, Action>, ILexicalScope, Action>, ILexicalScope, Func> where TDelegate : Delegate { private readonly Type returnType; @@ -148,4 +149,4 @@ public override void Dispose() recursion = null; base.Dispose(); } -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs index 1ceeabeb65..54959c4149 100644 --- a/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs +++ b/src/DotNext.Metaprogramming/Metaprogramming/MatchBuilder.cs @@ -157,6 +157,7 @@ public MatchBuilder Case(Type expectedType, CaseStatement body) public MatchBuilder Case(CaseStatement body) => Case(typeof(T), body); + [RequiresUnreferencedCode("Dynamically accesses properties/fields named in .")] private static Pattern StructuralPattern(IEnumerable<(string, Expression)> structPattern) => obj => { @@ -170,6 +171,7 @@ private static Pattern StructuralPattern(IEnumerable<(string, Expression)> struc return result ?? Expression.Constant(false); }; + [RequiresUnreferencedCode("Dynamically accesses properties/fields named in .")] private MatchBuilder Case(IEnumerable<(string, Expression)> structPattern, CaseStatement body) => Case(StructuralPattern(structPattern), body); @@ -180,6 +182,7 @@ private MatchBuilder Case(IEnumerable<(string, Expression)> structPattern, CaseS /// The expected value of the field or property. /// The action to be executed if object matches to the pattern. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses property/field .")] public MatchBuilder Case(string memberName, Expression memberValue, Func body) => Case(StructuralPattern(List.Singleton((memberName, memberValue))), value => body(Expression.PropertyOrField(value, memberName))); @@ -192,6 +195,7 @@ public MatchBuilder Case(string memberName, Expression memberValue, FuncThe expected value of the second field or property. /// The action to be executed if object matches to the pattern. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses properties/fields , .")] public MatchBuilder Case(string memberName1, Expression memberValue1, string memberName2, Expression memberValue2, Func body) => Case(StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2)]), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2))); @@ -206,6 +210,7 @@ public MatchBuilder Case(string memberName1, Expression memberValue1, string mem /// The expected value of the third field or property. /// The action to be executed if object matches to the pattern. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses properties/fields , , .")] public MatchBuilder Case(string memberName1, Expression memberValue1, string memberName2, Expression memberValue2, string memberName3, Expression memberValue3, Func body) => Case(StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3)]), value => body(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2), Expression.PropertyOrField(value, memberName3))); @@ -239,6 +244,7 @@ private static (string, Expression) GetMemberPattern(object @this, string member /// The structure pattern represented by instance of anonymous type. /// The action to be executed if object matches to the pattern. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses properties/fields represented in .")] public MatchBuilder Case(object structPattern, CaseStatement body) => Case(GetProperties(structPattern), body); @@ -248,6 +254,7 @@ public MatchBuilder Case(object structPattern, CaseStatement body) /// The structure pattern represented by instance of anonymous type. /// The value to be supplied if the specified structural pattern matches to the passed object. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses properties/fields represented in .")] public MatchBuilder Case(object structPattern, Expression value) => Case(structPattern, new CaseStatement(value.TrivialCaseStatement)); @@ -313,6 +320,7 @@ public MatchBuilder Case(Pattern pattern, Action body) /// The structure pattern represented by instance of anonymous type. /// The action to be executed if object matches to the pattern. /// this builder. + [RequiresUnreferencedCode("Dynamically accesses fields represented in .")] public MatchBuilder Case(object structPattern, Action body) { using var statement = new MatchByConditionStatement(this, StructuralPattern(GetProperties(structPattern))); @@ -483,6 +491,7 @@ internal CaseStatementBuilder(MatchByMemberStatement statement, string memberNam this.statement = statement; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] Expression ISupplier.Invoke(ParameterExpression value) { memberHandler(Expression.PropertyOrField(value, memberName)); @@ -500,6 +509,7 @@ internal MatchByMemberStatement(MatchBuilder builder, string memberName, Express this.memberValue = memberValue; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { var pattern = StructuralPattern(List.Singleton((memberName, memberValue))); @@ -524,6 +534,7 @@ internal CaseStatementBuilder(MatchByTwoMembersStatement statement, string membe this.statement = statement; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] Expression ISupplier.Invoke(ParameterExpression value) { memberHandler(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2)); @@ -543,6 +554,7 @@ internal MatchByTwoMembersStatement(MatchBuilder builder, string memberName1, Ex this.memberValue2 = memberValue2; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { var pattern = StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2)]); @@ -568,6 +580,7 @@ internal CaseStatementBuilder(MatchByThreeMembersStatement statement, string mem this.statement = statement; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] Expression ISupplier.Invoke(ParameterExpression value) { memberHandler(Expression.PropertyOrField(value, memberName1), Expression.PropertyOrField(value, memberName2), Expression.PropertyOrField(value, memberName3)); @@ -589,6 +602,7 @@ internal MatchByThreeMembersStatement(MatchBuilder builder, string memberName1, this.memberValue3 = memberValue3; } + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Public methods on MatchBuilder are annotated appropriately.")] private protected override MatchBuilder Build(MatchBuilder builder, Action scope) { var pattern = StructuralPattern([(memberName1, memberValue1), (memberName2, memberValue2), (memberName3, memberValue3)]); @@ -635,4 +649,4 @@ file static class ExpressionHelpers [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313", Justification = "Underscore is used to indicate that the parameter is unused")] internal static Expression TrivialCaseStatement(this Expression value, ParameterExpression _) => value; -} \ No newline at end of file +} diff --git a/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs b/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs index da11274d4f..64cf00ed8d 100644 --- a/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs +++ b/src/DotNext.Metaprogramming/Runtime/CompilerServices/TaskType.cs @@ -54,4 +54,4 @@ internal MethodCallExpression AdjustTaskType(MethodCallExpression startMachineCa internal bool IsValueTask => taskType is { IsValueType: true }; public static implicit operator Type(in TaskType type) => type.taskType ?? typeof(Task); -} \ No newline at end of file +}