From 86223a11c9ee1ffb8eeabe4ec042c2651a81659c Mon Sep 17 00:00:00 2001
From: Koen Bekkenutte <2912652+kbekkenutte@users.noreply.github.com>
Date: Mon, 18 Oct 2021 22:36:06 +0800
Subject: [PATCH] support for rewriting null conditional access expressions
---
.../NullConditionalRewriteSupport.cs | 30 +++
.../ProjectableAttribute.cs | 1 +
...rameworkCore.Projectables.Generator.csproj | 1 +
.../ExpressionSyntaxRewriter.cs | 67 ++++-
.../ProjectableInterpreter.cs | 17 +-
.../ExtensionsMethods/EntityExtensions.cs | 4 +-
.../NullConditionals/Entity.cs | 11 +
.../NullConditionals/EntityExtensions.cs | 31 +++
...Tests.ComplexMemberExpression.verified.txt | 2 +
...iteTests.RelationalExpression.verified.txt | 4 +
...eTests.SimpleMemberExpression.verified.txt | 2 +
.../IngoreNullConditionalRewriteTests.cs | 48 ++++
...Tests.ComplexMemberExpression.verified.txt | 2 +
...iteTests.RelationalExpression.verified.txt | 5 +
...eTests.SimpleMemberExpression.verified.txt | 2 +
.../RewriteNullConditionalRewriteTests.cs | 48 ++++
...nExpressionGeneratorTests.Foo.verified.txt | 15 ++
...gnoreSupport_IsBeingRewritten.verified.txt | 15 ++
...writeSupport_IsBeingRewritten.verified.txt | 15 ++
...gnoreSupport_IsBeingRewritten.verified.txt | 14 +
...writeSupport_IsBeingRewritten.verified.txt | 14 +
...gnoreSupport_IsBeingRewritten.verified.txt | 14 +
...writeSupport_IsBeingRewritten.verified.txt | 14 +
...writeSupport_IsBeingRewritten.verified.txt | 15 ++
...writeSupport_IsBeingRewritten.verified.txt | 14 +
...writeSupport_IsBeingRewritten.verified.txt | 14 +
...gnoreSupport_IsBeingRewritten.verified.txt | 14 +
...writeSupport_IsBeingRewritten.verified.txt | 14 +
.../ProjectionExpressionGeneratorTests.cs | 246 +++++++++++++++++-
29 files changed, 682 insertions(+), 11 deletions(-)
create mode 100644 src/EntityFrameworkCore.Projectables.Abstractions/NullConditionalRewriteSupport.cs
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/Entity.cs
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/EntityExtensions.cs
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.RelationalExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.cs
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.RelationalExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.cs
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.Foo.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithExplicitNullCheckRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreRewriteSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
create mode 100644 tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
diff --git a/src/EntityFrameworkCore.Projectables.Abstractions/NullConditionalRewriteSupport.cs b/src/EntityFrameworkCore.Projectables.Abstractions/NullConditionalRewriteSupport.cs
new file mode 100644
index 0000000..13d836a
--- /dev/null
+++ b/src/EntityFrameworkCore.Projectables.Abstractions/NullConditionalRewriteSupport.cs
@@ -0,0 +1,30 @@
+namespace EntityFrameworkCore.Projectables
+{
+ ///
+ /// Configures how null-conditional operators are handeled
+ ///
+ public enum NullConditionalRewriteSupport
+ {
+ ///
+ /// Don't rewrite null conditional operators (Default behavior).
+ /// Usage of null conditional operators is thereby not allowed
+ ///
+ None,
+
+ ///
+ /// Ignore null-conditional operators in the generated expression tree
+ ///
+ ///
+ /// (A?.B) is rewritten as expression: (A.B)
+ ///
+ Ignore,
+
+ ///
+ /// Translates null-conditional operators into explicit null checks
+ ///
+ ///
+ /// (A?.B) is rewritten as expression: (A != null ? A.B : null)
+ ///
+ Rewrite
+ }
+}
diff --git a/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs b/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
index 7fd5ffc..dd4a368 100644
--- a/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
+++ b/src/EntityFrameworkCore.Projectables.Abstractions/ProjectableAttribute.cs
@@ -9,5 +9,6 @@ namespace EntityFrameworkCore.Projectables
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = true, AllowMultiple = false)]
public sealed class ProjectableAttribute : Attribute
{
+ public NullConditionalRewriteSupport NullConditionalRewriteSupport { get; set; }
}
}
diff --git a/src/EntityFrameworkCore.Projectables.Generator/EntityFrameworkCore.Projectables.Generator.csproj b/src/EntityFrameworkCore.Projectables.Generator/EntityFrameworkCore.Projectables.Generator.csproj
index 4c40f73..ee5dc40 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/EntityFrameworkCore.Projectables.Generator.csproj
+++ b/src/EntityFrameworkCore.Projectables.Generator/EntityFrameworkCore.Projectables.Generator.csproj
@@ -7,6 +7,7 @@
+
diff --git a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
index 311c62e..951e995 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs
@@ -14,11 +14,76 @@ namespace EntityFrameworkCore.Projectables.Generator
{
readonly INamedTypeSymbol _targetTypeSymbol;
readonly SemanticModel _semanticModel;
+ readonly NullConditionalRewriteSupport _nullConditionalRewriteSupport;
+ readonly Stack _conditionalAccessExpressionsStack = new();
- public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, SemanticModel semanticModel)
+ public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, SemanticModel semanticModel, NullConditionalRewriteSupport nullConditionalRewriteSupport)
{
_targetTypeSymbol = targetTypeSymbol;
_semanticModel = semanticModel;
+ _nullConditionalRewriteSupport = nullConditionalRewriteSupport;
+ }
+
+ public override SyntaxNode? VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node)
+ {
+ var targetExpression = (ExpressionSyntax)Visit(node.Expression);
+
+ _conditionalAccessExpressionsStack.Push(targetExpression);
+
+ return _nullConditionalRewriteSupport switch {
+ NullConditionalRewriteSupport.Ignore => Visit(node.WhenNotNull),
+ NullConditionalRewriteSupport.Rewrite =>
+ SyntaxFactory.ConditionalExpression(
+ SyntaxFactory.BinaryExpression(
+ SyntaxKind.NotEqualsExpression,
+ targetExpression
+ .WithTrailingTrivia(SyntaxFactory.Whitespace(" ")),
+ SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)
+ .WithLeadingTrivia(SyntaxFactory.Whitespace(" "))
+ )
+ .WithTrailingTrivia(SyntaxFactory.Whitespace(" ")),
+ SyntaxFactory.ParenthesizedExpression(
+ (ExpressionSyntax)Visit(node.WhenNotNull)
+ )
+ .WithLeadingTrivia(SyntaxFactory.Whitespace(" "))
+ .WithTrailingTrivia(SyntaxFactory.Whitespace(" ")),
+ SyntaxFactory.LiteralExpression(SyntaxKind.NullLiteralExpression)
+ .WithLeadingTrivia(SyntaxFactory.Whitespace(" "))
+ ),
+ _ => base.VisitConditionalAccessExpression(node)
+ };
+ }
+
+ public override SyntaxNode? VisitMemberBindingExpression(MemberBindingExpressionSyntax node)
+ {
+ if (_conditionalAccessExpressionsStack.Count == 0)
+ {
+ throw new InvalidOperationException("Expected at least one conditional expression on the stack");
+ }
+
+ var targetExpression = _conditionalAccessExpressionsStack.Pop();
+
+ return _nullConditionalRewriteSupport switch {
+ NullConditionalRewriteSupport.Ignore => SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, targetExpression, node.Name),
+ NullConditionalRewriteSupport.Rewrite => SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, targetExpression, node.Name),
+ _ => node
+ };
+ }
+
+ public override SyntaxNode? VisitElementBindingExpression(ElementBindingExpressionSyntax node)
+ {
+ if (_conditionalAccessExpressionsStack.Count == 0)
+ {
+ throw new InvalidOperationException("Expected at least one conditional expression on the stack");
+ }
+
+ var targetExpression = _conditionalAccessExpressionsStack.Pop();
+
+ return _nullConditionalRewriteSupport switch {
+ NullConditionalRewriteSupport.Ignore => SyntaxFactory.ElementAccessExpression(targetExpression, node.ArgumentList),
+ NullConditionalRewriteSupport.Rewrite => SyntaxFactory.ElementAccessExpression(targetExpression, node.ArgumentList),
+ _ => Visit(node)
+ };
}
public override SyntaxNode? VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
diff --git a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
index 4b400e1..50f97d7 100644
--- a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
+++ b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
@@ -31,7 +32,6 @@ namespace EntityFrameworkCore.Projectables.Generator
return null;
}
-
var projectableAttributeTypeSymbol = context.Compilation.GetTypeByMetadataName("EntityFrameworkCore.Projectables.ProjectableAttribute");
var projectableAttributeClass = memberSymbol.GetAttributes()
@@ -43,7 +43,15 @@ namespace EntityFrameworkCore.Projectables.Generator
return null;
}
- var expressionSyntaxRewriter = new ExpressionSyntaxRewriter(memberSymbol.ContainingType, semanticModel);
+ var nullConditionalRewriteSupport = projectableAttributeClass.NamedArguments
+ .Where(x => x.Key == "NullConditionalRewriteSupport")
+ .Where(x => x.Value.Kind == TypedConstantKind.Enum)
+ .Select(x => x.Value.Value)
+ .Where(x => Enum.IsDefined(typeof(NullConditionalRewriteSupport), x))
+ .Cast()
+ .FirstOrDefault();
+
+ var expressionSyntaxRewriter = new ExpressionSyntaxRewriter(memberSymbol.ContainingType, semanticModel, nullConditionalRewriteSupport);
var parameterSyntaxRewriter = new ParameterSyntaxRewriter(semanticModel);
var returnTypeSyntaxRewriter = new ReturnTypeSyntaxRewriter(semanticModel);
@@ -128,8 +136,6 @@ namespace EntityFrameworkCore.Projectables.Generator
return null;
}
-
-
descriptor.UsingDirectives =
memberDeclarationSyntax.SyntaxTree
.GetRoot()
@@ -137,7 +143,6 @@ namespace EntityFrameworkCore.Projectables.Generator
.OfType()
.Select(x => x.ToString());
-
return descriptor;
}
}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/ExtensionsMethods/EntityExtensions.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/ExtensionsMethods/EntityExtensions.cs
index 60f29f3..61d1690 100644
--- a/tests/EntityFrameworkCore.Projectables.FunctionalTests/ExtensionsMethods/EntityExtensions.cs
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/ExtensionsMethods/EntityExtensions.cs
@@ -11,8 +11,8 @@ namespace EntityFrameworkCore.Projectables.FunctionalTests.ExtensionMethods
[Projectable]
public static int Foo(this Entity entity) => entity.Id + 1;
- [Projectable]
- public static int Foo2(this Entity entity) => entity.Foo() + 1;
+ [Projectable]
+ public static int Foo2(this Entity entity) => entity.Foo() + 1;
[Projectable]
public static Entity? LeadingEntity(this Entity entity, DbContext dbContext)
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/Entity.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/Entity.cs
new file mode 100644
index 0000000..2de9fb6
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/Entity.cs
@@ -0,0 +1,11 @@
+using System.Collections.Generic;
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.NullConditionals
+{
+ public record Entity
+ {
+ public int Id { get; set; }
+ public string? Name { get; set; }
+ public List? RelatedEntities { get; set; }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/EntityExtensions.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/EntityExtensions.cs
new file mode 100644
index 0000000..8fa5499
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/EntityExtensions.cs
@@ -0,0 +1,31 @@
+#nullable disable
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.NullConditionals
+{
+ public static class EntityExtensions
+ {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static string GetNameIgnoreNulls(this Entity entity)
+ => entity?.Name;
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static int? GetNameLengthIgnoreNulls(this Entity entity)
+ => entity.GetNameIgnoreNulls()?.Length;
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static Entity GetFirstRelatedIgnoreNulls(this Entity entity)
+ => entity?.RelatedEntities?[0];
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static string GetNameRewriteNulls(this Entity entity)
+ => entity?.Name;
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static int? GetNameLengthRewriteNulls(this Entity entity)
+ => entity.GetNameIgnoreNulls()?.Length;
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static Entity GetFirstRelatedRewriteNulls(this Entity entity)
+ => entity?.RelatedEntities?[0];
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.ComplexMemberExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
new file mode 100644
index 0000000..8b2d6b7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.RelationalExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.RelationalExpression.verified.txt
new file mode 100644
index 0000000..a80b047
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.RelationalExpression.verified.txt
@@ -0,0 +1,4 @@
+SELECT [e].[Id], [e0].[Id], [e0].[EntityId], [e0].[Name]
+FROM [Entity] AS [e]
+LEFT JOIN [Entity] AS [e0] ON [e].[Id] = [e0].[EntityId]
+ORDER BY [e].[Id]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.SimpleMemberExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
new file mode 100644
index 0000000..50627df
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
@@ -0,0 +1,2 @@
+SELECT [e].[Name]
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.cs
new file mode 100644
index 0000000..44e48d5
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/IngoreNullConditionalRewriteTests.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
+using Microsoft.EntityFrameworkCore;
+using VerifyXunit;
+using Xunit;
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.NullConditionals
+{
+ [UsesVerify]
+ public class IngoreNullConditionalRewriteTests
+ {
+ [Fact]
+ public Task SimpleMemberExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameIgnoreNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ComplexMemberExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameLengthIgnoreNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task RelationalExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetFirstRelatedIgnoreNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.ComplexMemberExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
new file mode 100644
index 0000000..8b2d6b7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.ComplexMemberExpression.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.RelationalExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.RelationalExpression.verified.txt
new file mode 100644
index 0000000..44b3cf9
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.RelationalExpression.verified.txt
@@ -0,0 +1,5 @@
+SELECT CAST(1 AS bit), [e].[Id], [e0].[Id], [e0].[EntityId], [e0].[Name], [e1].[Id], [e1].[EntityId], [e1].[Name]
+FROM [Entity] AS [e]
+LEFT JOIN [Entity] AS [e0] ON [e].[Id] = [e0].[EntityId]
+LEFT JOIN [Entity] AS [e1] ON [e].[Id] = [e1].[EntityId]
+ORDER BY [e].[Id], [e0].[Id]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.SimpleMemberExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
new file mode 100644
index 0000000..8b2d6b7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.SimpleMemberExpression.verified.txt
@@ -0,0 +1,2 @@
+SELECT CAST(LEN([e].[Name]) AS int)
+FROM [Entity] AS [e]
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.cs
new file mode 100644
index 0000000..9acdbfa
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/NullConditionals/RewriteNullConditionalRewriteTests.cs
@@ -0,0 +1,48 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using EntityFrameworkCore.Projectables.FunctionalTests.Helpers;
+using Microsoft.EntityFrameworkCore;
+using VerifyXunit;
+using Xunit;
+
+namespace EntityFrameworkCore.Projectables.FunctionalTests.NullConditionals
+{
+ [UsesVerify]
+ public class RewriteNullConditionalRewriteTests
+ {
+ [Fact]
+ public Task SimpleMemberExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameLengthRewriteNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task ComplexMemberExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetNameLengthRewriteNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+
+ [Fact]
+ public Task RelationalExpression()
+ {
+ using var dbContext = new SampleDbContext();
+
+ var query = dbContext.Set()
+ .Select(x => x.GetFirstRelatedRewriteNulls());
+
+ return Verifier.Verify(query.ToQueryString());
+ }
+ }
+}
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.Foo.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.Foo.verified.txt
new file mode 100644
index 0000000..a25a92e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.Foo.verified.txt
@@ -0,0 +1,15 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_EntityExtensions_GetFirstRelatedIgnoreNulls
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (Entity entity) => entity != null ? (entity.RelatedEntities != null ? (entity.RelatedEntities[0]) : null) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..5d463d7
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,15 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_EntityExtensions_GetFirstRelatedIgnoreNulls
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (Entity entity) => entity.RelatedEntities[0];
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..a25a92e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,15 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_EntityExtensions_GetFirstRelatedIgnoreNulls
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (Entity entity) => entity != null ? (entity.RelatedEntities != null ? (entity.RelatedEntities[0]) : null) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..223687e
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input[0].ToString();
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..37fb8ca
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input != null ? (input[0].ToString()) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..f9b3a83
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetLength
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input.Length;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..c729b9a
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableMemberBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetLength
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input != null ? (input.Length) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..5ee3ea1
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableParameters_WithRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,15 @@
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_EntityExtensions_GetFirstName
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (Entity entity) => entity.FullName != null ? (entity.FullName.Substring(entity.FullName != null ? (entity.FullName.IndexOf(' ') ) : null?? 0)) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithExplicitNullCheckRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithExplicitNullCheckRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..34c0099
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithExplicitNullCheckRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input != null ? (input[0]) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..811dbf2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input[0];
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..811dbf2
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithIgnoreSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input[0];
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
new file mode 100644
index 0000000..34c0099
--- /dev/null
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.NullableSimpleElementBinding_WithRewriteSupport_IsBeingRewritten.verified.txt
@@ -0,0 +1,14 @@
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+using Foo;
+
+namespace EntityFrameworkCore.Projectables.Generated
+#nullable disable
+{
+ public static class Foo_C_GetFirst
+ {
+ public static System.Linq.Expressions.Expression> Expression =>
+ (string input) => input != null ? (input[0]) : null;
+ }
+}
\ No newline at end of file
diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
index bc87af0..843c43b 100644
--- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
+++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs
@@ -487,6 +487,250 @@ namespace Foo {
return Verifier.Verify(result.GeneratedTrees[0].ToString());
}
+ [Fact]
+ public Task NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static int? GetLength(this string input) => input?.Length;
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableMemberBinding_WithRewriteSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static int? GetLength(this string input) => input?.Length;
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableSimpleElementBinding_WithIgnoreSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static char? GetFirst(this string input) => input?[0];
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableSimpleElementBinding_WithRewriteSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static char? GetFirst(this string input) => input?[0];
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+
+ [Fact]
+ public Task NullableElementBinding_WithIgnoreSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static string? GetFirst(this string input) => input?[0].ToString();
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableElementBinding_WithRewriteSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ static class C {
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static string? GetFirst(this string input) => input?[0].ToString();
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableElementAndMemberBinding_WithIgnoreSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ public static class EntityExtensions
+ {
+ public record Entity
+ {
+ public int Id { get; set; }
+ public List? RelatedEntities { get; set; }
+ }
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Ignore)]
+ public static Entity GetFirstRelatedIgnoreNulls(this Entity entity)
+ => entity?.RelatedEntities?[0];
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableElementAndMemberBinding_WithRewriteSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ public static class EntityExtensions
+ {
+ public record Entity
+ {
+ public int Id { get; set; }
+ public List? RelatedEntities { get; set; }
+ }
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static Entity GetFirstRelatedIgnoreNulls(this Entity entity)
+ => entity?.RelatedEntities?[0];
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
+ [Fact]
+ public Task NullableParameters_WithRewriteSupport_IsBeingRewritten()
+ {
+ var compilation = CreateCompilation(@"
+using System;
+using System.Linq;
+using System.Collections.Generic;
+using EntityFrameworkCore.Projectables;
+
+namespace Foo {
+ public static class EntityExtensions
+ {
+ public record Entity
+ {
+ public int Id { get; set; }
+ public string? FullName { get; set; }
+ }
+
+ [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)]
+ public static string GetFirstName(this Entity entity)
+ => entity.FullName?.Substring(entity.FullName?.IndexOf(' ') ?? 0);
+ }
+}
+");
+
+ var result = RunGenerator(compilation);
+
+ Assert.Empty(result.Diagnostics);
+ Assert.Single(result.GeneratedTrees);
+
+ return Verifier.Verify(result.GeneratedTrees[0].ToString());
+ }
+
#region Helpers
Compilation CreateCompilation(string source, bool expectedToCompile = true)
@@ -494,8 +738,6 @@ namespace Foo {
var references = Basic.Reference.Assemblies.NetStandard20.All.ToList();
references.Add(MetadataReference.CreateFromFile(typeof(ProjectableAttribute).Assembly.Location));
- var assemblyPath = Path.GetDirectoryName(typeof(object).Assembly.Location);
-
var compilation = CSharpCompilation.Create("compilation",
new[] { CSharpSyntaxTree.ParseText(source) },
references,