From 57bed8c3b199f41cbe4fea7211e22a158f9d0028 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Wed, 9 Sep 2026 19:28:57 +1000 Subject: [PATCH 1/2] fix(trimming): carry the rewriter's annotation on the type, not its overrides - RequiresUnreferencedCode on VisitBinary, VisitUnary and VisitMethodCall contradicts the ExpressionVisitor members they override, which a consumer's trimmed publish reports as IL2046 three times. The annotation belongs on the rewriter itself, where no base member disagrees with it. - Build the shared rewriter on first use rather than in a static initializer: a static constructor has nowhere to carry the annotation the type now needs, so Reflection.Rewrite states it instead. Every caller already reaches it from a path that says the same thing. Closes #84 --- .../Expression/ExpressionRewriter.cs | 8 ++------ .../Expression/Reflection.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/ReactiveUI.Binding.Shared/Expression/ExpressionRewriter.cs b/src/ReactiveUI.Binding.Shared/Expression/ExpressionRewriter.cs index beaa7bb7..aa480239 100644 --- a/src/ReactiveUI.Binding.Shared/Expression/ExpressionRewriter.cs +++ b/src/ReactiveUI.Binding.Shared/Expression/ExpressionRewriter.cs @@ -31,6 +31,8 @@ namespace ReactiveUI.Binding.Expressions; /// is stripped. /// /// +[RequiresUnreferencedCode( + "Expression rewriting uses reflection over runtime types which may be removed by trimming.")] internal sealed class ExpressionRewriter : ExpressionVisitor { /// @@ -141,8 +143,6 @@ internal Expression[] VisitArgumentList(ReadOnlyCollection arguments } /// - [RequiresUnreferencedCode( - "Expression rewriting uses reflection over runtime types which may be removed by trimming.")] protected override Expression VisitBinary(BinaryExpression node) { if (node.Right is not ConstantExpression) @@ -159,8 +159,6 @@ protected override Expression VisitBinary(BinaryExpression node) } /// - [RequiresUnreferencedCode( - "Expression rewriting uses reflection over runtime types which may be removed by trimming.")] protected override Expression VisitUnary(UnaryExpression node) { // Visit() only routes Convert and ArrayLength here, so no fallthrough is needed. @@ -178,8 +176,6 @@ protected override Expression VisitUnary(UnaryExpression node) } /// - [RequiresUnreferencedCode( - "Expression rewriting uses reflection over runtime types which may be removed by trimming.")] protected override Expression VisitMethodCall(MethodCallExpression node) { if (!node.Method.IsSpecialName || !AllConstant(node.Arguments)) diff --git a/src/ReactiveUI.Binding.Shared/Expression/Reflection.cs b/src/ReactiveUI.Binding.Shared/Expression/Reflection.cs index c4a1c2a0..94e4c82b 100644 --- a/src/ReactiveUI.Binding.Shared/Expression/Reflection.cs +++ b/src/ReactiveUI.Binding.Shared/Expression/Reflection.cs @@ -15,17 +15,24 @@ namespace ReactiveUI.Binding.Expressions; /// Helper class for handling reflection and expression-tree related operations. public static class Reflection { - /// Singleton instance of the used for rewriting expression trees. + /// Reported when an expression yields no chain to walk. private const string EmptyExpressionChainMessage = "Expression chain must contain at least one element."; /// The shared rewriter instance used to simplify expressions before inspection. - private static readonly ExpressionRewriter ExpressionRewriterInstance = new(); + /// + /// Built on first use rather than in a static initializer. The rewriter reads runtime types by + /// reflection and says so, and a static constructor has nowhere to carry that annotation. + /// + private static ExpressionRewriter? _expressionRewriter; /// Uses the expression re-writer to simplify the expression down to its simplest expression. /// The expression to rewrite. /// The rewritten expression. + [RequiresUnreferencedCode( + "Expression rewriting uses reflection over runtime types which may be removed by trimming.")] [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Expression Rewrite(Expression? expression) => ExpressionRewriterInstance.Visit(expression); + public static Expression Rewrite(Expression? expression) => + (_expressionRewriter ??= new()).Visit(expression); /// Converts an expression that points to a property chain into a dotted path string. /// The expression to generate the property names from. From da441b2d28b2a67d3a172d0bd48463144d8fd7f5 Mon Sep 17 00:00:00 2001 From: Glenn Watson <5834289+glennawatson@users.noreply.github.com> Date: Wed, 9 Sep 2026 19:28:57 +1000 Subject: [PATCH 2/2] fix(build): keep the repository-only analyzers out of the package graph - Three analyzer references carried no PrivateAssets, so they were published as dependencies of every packed project rather than staying a build-time concern. - Affects the runtime and platform packages too, not only the generator package. Closes #82 --- src/Directory.Build.props | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Directory.Build.props b/src/Directory.Build.props index cd67e553..3c14bb2d 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -145,8 +145,8 @@ - - - + + +