Skip to content

Add support for nullable enums in expressive properties#61

Merged
koenbeuk merged 1 commit intomainfrom
fix/issue-59
May 7, 2026
Merged

Add support for nullable enums in expressive properties#61
koenbeuk merged 1 commit intomainfrom
fix/issue-59

Conversation

@koenbeuk
Copy link
Copy Markdown
Collaborator

@koenbeuk koenbeuk commented May 7, 2026

fixes #59

Copilot AI review requested due to automatic review settings May 7, 2026 00:13
@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds generator + integration coverage for expanding expressive properties involving nullable enum method calls (e.g., Trend?.ToString() and nullable-enum extension methods) so these can be expressed as expandable expression trees.

Changes:

  • Update enum method expansion emission to lift per-arm enum constants to Nullable<TEnum> when the receiver is nullable.
  • Add an integration test covering ToString() on a nullable enum within an [Expressive] property.
  • Add generator verification tests + verified outputs for nullable-enum ToString() and nullable-enum extension method expansion.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
tests/ExpressiveSharp.IntegrationTests/Tests/EnumComparisonTests.cs Adds runtime test and expressive property for ToString() on nullable enum.
tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/EnumTests.cs Adds generator tests for nullable-enum ToString() and nullable-enum extension method expansion.
tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/EnumTests.ExpandToStringOnNullableEnum.verified.txt Adds golden output for nullable-enum ToString() expansion.
tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/EnumTests.ExpandExtensionMethodOnNullableEnum.verified.txt Adds golden output for nullable-enum extension method expansion.
src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs Lifts enum constants to nullable receiver type during enum method expansion.
Comments suppressed due to low confidence (1)

src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs:823

  • For nullable enum receivers, the null wrapper currently returns defaultVar when the receiver is null. This changes semantics for methods that have a defined null behavior, e.g. Nullable<TEnum>.ToString() (returns "" when HasValue is false) and extension methods like the new Trend?.Describe() example (should return "n/a" on null, but the expansion yields null). Instead of returning defaultVar for the null branch, compute the call result for a null receiver/argument (using a Constant(null, typeof(Nullable<TEnum>)) as the receiver or first argument, matching static vs instance) and return that for the null branch so the expansion preserves runtime behavior.
        // For Nullable<TEnum>, wrap in: receiver == null ? default : chain
        if (isNullable)
        {
            var nullConst = NextVar();
            AppendLine($"var {nullConst} = {Expr}.Constant(null, typeof({receiverTypeFqn}));");

            var nullCheck = NextVar();
            AppendLine($"var {nullCheck} = {Expr}.Equal({receiverVar}, {nullConst});");

            var wrappedVar = NextVar();
            AppendLine($"var {wrappedVar} = {Expr}.Condition({nullCheck}, {defaultVar}, {currentVar}, typeof({returnTypeFqn}));");
            currentVar = wrappedVar;
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +362 to +391
[TestMethod]
public Task ExpandExtensionMethodOnNullableEnum()
{
var compilation = CreateCompilation(
"""
namespace Foo {
public enum Trend { Increase, Decrease, NoChange }

public static class TrendExtensions
{
public static string Describe(this Trend? value) =>
value.HasValue ? value.Value.ToString() : "n/a";
}

public record Entity
{
public Trend? Trend { get; init; }

[Expressive]
public string TrendLabel => Trend.Describe();
}
}
""");
var result = RunExpressiveGenerator(compilation);

Assert.AreEqual(0, result.Diagnostics.Length);
Assert.AreEqual(1, result.GeneratedTrees.Length);

return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
@koenbeuk koenbeuk merged commit 3011815 into main May 7, 2026
21 checks passed
@koenbeuk koenbeuk deleted the fix/issue-59 branch May 7, 2026 00:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ToString() on Nullable<TEnum> generates an invalid Expression.Call

2 participants