diff --git a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md index cb4c8a8..ef168b8 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md +++ b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md @@ -2,3 +2,4 @@ Rule ID | Category | Severity | Notes --------|----------|----------|-------------------- +EFP0002 | Design | Error | diff --git a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs index 5fb8d73..6623851 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs @@ -17,5 +17,13 @@ namespace EntityFrameworkCore.Projectables.Generator DiagnosticSeverity.Error, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor NullConditionalRewriteUnsupported = new( + id: "EFP0002", + title: "Method or property is not configured to support null-conditional expressions.", + messageFormat: "'{0}' has a null-conditional expression exposed but is not configured to rewrite this. Consider configuring a strategy using the NullConditionalRewriteSupport property on the Projectable attribute", + category: "Design", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + } } diff --git a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs index 951e995..ca101e7 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs @@ -15,13 +15,15 @@ namespace EntityFrameworkCore.Projectables.Generator readonly INamedTypeSymbol _targetTypeSymbol; readonly SemanticModel _semanticModel; readonly NullConditionalRewriteSupport _nullConditionalRewriteSupport; + readonly GeneratorExecutionContext _context; readonly Stack _conditionalAccessExpressionsStack = new(); - public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, SemanticModel semanticModel, NullConditionalRewriteSupport nullConditionalRewriteSupport) + public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, SemanticModel semanticModel, NullConditionalRewriteSupport nullConditionalRewriteSupport, GeneratorExecutionContext context) { _targetTypeSymbol = targetTypeSymbol; _semanticModel = semanticModel; _nullConditionalRewriteSupport = nullConditionalRewriteSupport; + _context = context; } public override SyntaxNode? VisitConditionalAccessExpression(ConditionalAccessExpressionSyntax node) @@ -30,6 +32,12 @@ namespace EntityFrameworkCore.Projectables.Generator _conditionalAccessExpressionsStack.Push(targetExpression); + if (_nullConditionalRewriteSupport == NullConditionalRewriteSupport.None) + { + var diagnostic = Diagnostic.Create(Diagnostics.NullConditionalRewriteUnsupported, node.GetLocation(), node); + _context.ReportDiagnostic(diagnostic); + } + return _nullConditionalRewriteSupport switch { NullConditionalRewriteSupport.Ignore => Visit(node.WhenNotNull), NullConditionalRewriteSupport.Rewrite => diff --git a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs index 50f97d7..7e76686 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs @@ -51,7 +51,7 @@ namespace EntityFrameworkCore.Projectables.Generator .Cast() .FirstOrDefault(); - var expressionSyntaxRewriter = new ExpressionSyntaxRewriter(memberSymbol.ContainingType, semanticModel, nullConditionalRewriteSupport); + var expressionSyntaxRewriter = new ExpressionSyntaxRewriter(memberSymbol.ContainingType, semanticModel, nullConditionalRewriteSupport, context); var parameterSyntaxRewriter = new ParameterSyntaxRewriter(semanticModel); var returnTypeSyntaxRewriter = new ReturnTypeSyntaxRewriter(semanticModel); diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs index 843c43b..4c5b777 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs @@ -487,6 +487,27 @@ namespace Foo { return Verifier.Verify(result.GeneratedTrees[0].ToString()); } + [Fact] + public void NullableMemberBinding_WithoutSupport_IsBeingReported() + { + var compilation = CreateCompilation(@" +using System; +using System.Linq; +using EntityFrameworkCore.Projectables; + +namespace Foo { + static class C { + [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.None)] + public static int? GetLength(this string input) => input?.Length; + } +} +"); + var result = RunGenerator(compilation); + + var diagnostic = Assert.Single(result.Diagnostics); + Assert.Equal("EFP0002", diagnostic.Id); + } + [Fact] public Task NullableMemberBinding_WithIgnoreSupport_IsBeingRewritten() {