Skip to content

Commit 085dc47

Browse files
committed
Broaden expression eval fallback and remove warnings
1 parent 419778f commit 085dc47

3 files changed

Lines changed: 25 additions & 7 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,4 +419,5 @@ async\method: Suffix with `Async` =\> `GetDataAsync()`
419419
- Expression compilation can emit invalid IL on some runtimes; fall back to `Compile(preferInterpretation: true)` when evaluating expressions to avoid CI-only `InvalidProgramException`.
420420
- Byref-like expression trees (e.g., `Span<T>`, `ReadOnlySpan<T>`) cannot be boxed; convert them to arrays before evaluation to avoid interpreter type load errors.
421421
- Reflection helpers should tolerate missing overloads (e.g., `MemoryExtensions.ToArray`) to prevent type initializer failures on runtimes where a method shape differs.
422+
- Compilation can fail with `TypeLoadException`/`ArgumentException` in addition to `InvalidProgramException`; catch broadly and fall back to interpreted compilation or a safe null-returning delegate.
422423
- `NUnit.Assert.ThrowsAsync<T>()` returns the exception instance (not a `Task`), so async tests should await a real async assertion (e.g., `action.Should().ThrowAsync<T>()`) to avoid CS1998 warnings.

learnings.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ This document is organized by topic to consolidate key learnings about the proje
130130
- **Expression.Compile InvalidProgramException:** Expression compilation can emit invalid IL on some runtimes; prefer `Compile(preferInterpretation: true)` or catch `InvalidProgramException` and recompile in interpreted mode to ensure value extraction works on CI
131131
- **ByRef-like expression evaluation:** Ref structs (e.g., `Span<T>`, `ReadOnlySpan<T>`) cannot be boxed to `object`; convert them to arrays (e.g., `MemoryExtensions.ToArray`) before value extraction to avoid interpreter `TypeLoadException`/`ArgumentException`.
132132
- **Reflection resilience:** Do not assume specific overloads exist across runtimes; when reflecting for helpers like `MemoryExtensions.ToArray`, select via tolerant predicate and allow null fallback to avoid type initializer failures.
133+
- **Compilation fallback breadth:** Expression compilation can also fail with `TypeLoadException`/`ArgumentException` on byref-like conversions; wrap compilation and fall back to interpreted mode or a null-returning delegate when both paths fail.
133134
- **NUnit Assert.ThrowsAsync return type:** `Assert.ThrowsAsync<T>()` returns the exception instance, not a `Task`, so async test methods need an explicit awaitable (e.g., `action.Should().ThrowAsync<T>()`) to avoid CS1998 warnings.
134135

135136
## String Diffing Implementation (Increment 5)

src/SharpAssert.Runtime/Features/Shared/ExpressionValueEvaluator.cs

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace SharpAssert.Features.Shared;
1010
static class ExpressionValueEvaluator
1111
{
1212
static readonly MethodInfo? ToArrayMethod = ResolveToArrayMethod();
13+
static readonly Func<object> NullEvaluator = () => null!;
1314

1415
public static object? Evaluate(Expression expression) => Compile(expression)();
1516

@@ -22,9 +23,16 @@ public static Func<object> Compile(Expression expression)
2223
{
2324
return lambda.Compile();
2425
}
25-
catch (InvalidProgramException)
26+
catch (Exception ex) when (IsCompileFallbackCase(ex))
2627
{
27-
return lambda.Compile(preferInterpretation: true);
28+
try
29+
{
30+
return lambda.Compile(preferInterpretation: true);
31+
}
32+
catch
33+
{
34+
return NullEvaluator;
35+
}
2836
}
2937
}
3038

@@ -68,12 +76,20 @@ static Expression Normalize(Expression expression)
6876
? expression
6977
: Expression.Convert(expression, readOnlySpanType);
7078

71-
if (ToArrayMethod == null)
72-
return Expression.Constant(null, typeof(object));
79+
if (ToArrayMethod != null)
80+
{
81+
var toArray = ToArrayMethod.MakeGenericMethod(elementType);
82+
var call = Expression.Call(toArray, spanExpression);
83+
return Expression.Convert(call, typeof(object));
84+
}
7385

74-
var toArray = ToArrayMethod.MakeGenericMethod(elementType);
75-
var call = Expression.Call(toArray, spanExpression);
86+
return Expression.Convert(expression, typeof(object));
87+
}
7688

77-
return Expression.Convert(call, typeof(object));
89+
static bool IsCompileFallbackCase(Exception ex)
90+
{
91+
return ex is InvalidProgramException
92+
or TypeLoadException
93+
or ArgumentException;
7894
}
7995
}

0 commit comments

Comments
 (0)