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 @@
-
-
-
+
+
+