diff --git a/README.md b/README.md index ab688bf..2f06978 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,36 @@ GROUP BY (COALESCE("u"."FirstName", '') || ' ') || COALESCE("u"."LastName", '') ORDER BY (COALESCE("u"."FirstName", '') || ' ') || COALESCE("u"."LastName", '') ``` +#### Can I use block-bodied methods instead of expression-bodied methods? +Yes! As of version 6.x, you can now use traditional block-bodied methods with `[Projectable]`. This makes code more readable when dealing with complex conditional logic: + +```csharp +// Expression-bodied (still supported) +[Projectable] +public string Level() => Value > 100 ? "High" : Value > 50 ? "Medium" : "Low"; + +// Block-bodied (now also supported!) +[Projectable] +public string Level() +{ + if (Value > 100) + return "High"; + else if (Value > 50) + return "Medium"; + else + return "Low"; +} +``` + +Both generate identical SQL. Block-bodied methods support: +- If-else statements (converted to ternary/CASE expressions) +- Switch statements +- Local variables (automatically inlined) +- Simple return statements + +The generator will also detect and report side effects (assignments, method calls to non-projectable methods, etc.) with precise error messages. See [Block-Bodied Methods Documentation](docs/BlockBodiedMethods.md) for complete details. + + #### How do I expand enum extension methods? When you have an enum property and want to call an extension method on it (like getting a display name from a `[Display]` attribute), you can use the `ExpandEnumMethods` property on the `[Projectable]` attribute. This will expand the enum method call into a chain of ternary expressions for each enum value, allowing EF Core to translate it to SQL CASE expressions. diff --git a/docs/BlockBodiedMethods.md b/docs/BlockBodiedMethods.md new file mode 100644 index 0000000..12c0022 --- /dev/null +++ b/docs/BlockBodiedMethods.md @@ -0,0 +1,383 @@ +# Block-Bodied Methods Support + +As of this version, EntityFrameworkCore.Projectables now supports "classic" block-bodied methods decorated with `[Projectable]`, in addition to expression-bodied methods. + +## What's Supported + +Block-bodied methods can now be transformed into expression trees when they contain: + +### 1. Simple Return Statements +```csharp +[Projectable] +public int GetConstant() +{ + return 42; +} +``` + +### 2. If-Else Statements (converted to ternary expressions) +```csharp +[Projectable] +public string GetCategory() +{ + if (Value > 100) + { + return "High"; + } + else + { + return "Low"; + } +} +``` + +### 3. Nested If-Else Statements +```csharp +[Projectable] +public string GetLevel() +{ + if (Value > 100) + { + return "High"; + } + else if (Value > 50) + { + return "Medium"; + } + else + { + return "Low"; + } +} +``` + +### 4. Local Variable Declarations (inlined into the expression) +```csharp +[Projectable] +public int CalculateDouble() +{ + var doubled = Value * 2; + return doubled + 5; +} + +// Transitive inlining is also supported: +[Projectable] +public int CalculateComplex() +{ + var a = Value * 2; + var b = a + 5; + return b + 10; // Fully expanded to: Value * 2 + 5 + 10 +} +``` + +**⚠️ Important Notes:** +- Local variables are inlined at each usage point, which duplicates the initializer expression +- If a local variable is used multiple times, its initializer expression is duplicated at each usage, which can change semantics if the initializer has side effects +- Local variables can only be declared at the method body level, not inside nested blocks (if/switch/etc.) +- Variables are fully expanded transitively (variables that reference other variables are fully inlined) + +### 5. Switch Statements (converted to nested ternary expressions) +```csharp +[Projectable] +public string GetValueLabel() +{ + switch (Value) + { + case 1: + return "One"; + case 2: + return "Two"; + case 3: + return "Three"; + default: + return "Many"; + } +} +``` + +### 6. If Statements Without Else (uses default value) +```csharp +// Pattern 1: Explicit null return +[Projectable] +public int? GetPremiumIfActive() +{ + if (IsActive) + { + return Value * 2; + } + return null; // Explicit return for all code paths +} + +// Pattern 2: Explicit fallback return +[Projectable] +public string GetStatus() +{ + if (IsActive) + { + return "Active"; + } + return "Inactive"; // Explicit fallback +} +``` + +### 7. Multiple Early Returns (converted to nested ternary expressions) +```csharp +[Projectable] +public string GetValueCategory() +{ + if (Value > 100) + { + return "Very High"; + } + + if (Value > 50) + { + return "High"; + } + + if (Value > 10) + { + return "Medium"; + } + + return "Low"; +} + +// Converted to: Value > 100 ? "Very High" : (Value > 50 ? "High" : (Value > 10 ? "Medium" : "Low")) +``` + +## Limitations and Warnings + +The source generator will produce **warning EFP0003** when it encounters unsupported statements in block-bodied methods: + +### Unsupported Statements: +- While, for, foreach loops +- Try-catch-finally blocks +- Throw statements +- New object instantiation in statement position + +### Example of Unsupported Pattern: +```csharp +[Projectable] +public int GetValue() +{ + for (int i = 0; i < 10; i++) // ❌ Loops not supported + { + // ... + } + return 0; +} +``` + +Supported patterns: +```csharp +[Projectable] +public int GetValue() +{ + if (IsActive) // ✅ If without else is now supported! + { + return Value; + } + else + { + return 0; + } +} +``` + +Additional supported patterns: +```csharp +// If without else using fallback return: +[Projectable] +public int GetValue() +{ + if (IsActive) + { + return Value; + } + return 0; // ✅ Fallback return +} + +// Switch statement: +[Projectable] +public string GetLabel() +{ + switch (Value) // ✅ Switch statements now supported! + { + case 1: + return "One"; + case 2: + return "Two"; + default: + return "Other"; + } +} +``` + +Or as expression-bodied: +```csharp +[Projectable] +public int GetValue() => IsActive ? Value : 0; // ✅ Expression-bodied +``` + +## How It Works + +The source generator: +1. Parses block-bodied methods +2. Converts if-else statements to conditional (ternary) expressions +3. Converts switch statements to nested conditional expressions +4. Inlines local variables into the return expression +5. Rewrites the resulting expression using the existing expression transformation pipeline +6. Generates the same output as expression-bodied methods + +## Benefits + +- **More readable code**: Complex logic with nested conditions and switch statements is often easier to read than nested ternary operators +- **Gradual migration**: Existing code with block bodies can now be marked as `[Projectable]` without rewriting +- **Intermediate variables**: Local variables can make complex calculations more understandable +- **Switch support**: Traditional switch statements now work alongside switch expressions + +## SQL Output Examples + +### Switch Statement with Multiple Cases +Given this code: +```csharp +switch (Value) +{ + case 1: + case 2: + return "Low"; + case 3: + case 4: + case 5: + return "Medium"; + default: + return "High"; +} +``` + +Generates optimized SQL: +```sql +SELECT CASE + WHEN [e].[Value] IN (1, 2) THEN N'Low' + WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium' + ELSE N'High' +END +FROM [Entity] AS [e] +``` + +### If-Else Example Output + +Given this code: +```csharp +public record Entity +{ + public int Value { get; set; } + public bool IsActive { get; set; } + + [Projectable] + public int GetAdjustedValue() + { + if (IsActive && Value > 0) + { + return Value * 2; + } + else + { + return 0; + } + } +} +``` + +The generated SQL will be: +```sql +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 + THEN [e].[Value] * 2 + ELSE 0 +END +FROM [Entity] AS [e] +``` + +## Side Effect Detection + +The generator provides specific error reporting for side effects in block-bodied methods, helping you identify and fix issues quickly. + +### Detected Side Effects + +#### 1. Property Assignments (EFP0004 - Error) + +Property assignments modify state and are not allowed: + +```csharp +[Projectable] +public int Foo() +{ + Bar = 10; // ❌ Error: Assignment operation has side effects + return Bar; +} +``` + +#### 2. Compound Assignments (EFP0004 - Error) + +Compound assignment operators like `+=`, `-=`, `*=`, etc. are not allowed: + +```csharp +[Projectable] +public int Foo() +{ + Bar += 10; // ❌ Error: Compound assignment operator '+=' has side effects + return Bar; +} +``` + +#### 3. Increment/Decrement Operators (EFP0004 - Error) + +Pre and post increment/decrement operators are not allowed: + +```csharp +[Projectable] +public int Foo() +{ + var x = 5; + x++; // ❌ Error: Increment/decrement operator '++' has side effects + return x; +} +``` + +#### 4. Non-Projectable Method Calls (EFP0005 - Warning) + +Calls to methods not marked with `[Projectable]` may have side effects: + +```csharp +[Projectable] +public int Foo() +{ + Console.WriteLine("test"); // ⚠️ Warning: Method call 'WriteLine' may have side effects + return Bar; +} +``` + +### Diagnostic Codes + +- **EFP0003**: Unsupported statement in block-bodied method (Warning) +- **EFP0004**: Statement with side effects in block-bodied method (Error) +- **EFP0005**: Potential side effect in block-bodied method (Warning) + +### Error Message Improvements + +Instead of generic error messages, you now get precise, actionable feedback: + +**Before:** +``` +warning EFP0003: Method 'Foo' contains an unsupported statement: Expression statements are not supported +``` + +**After:** +``` +error EFP0004: Property assignment 'Bar' has side effects and cannot be used in projectable methods +``` + +The error message points to the exact line with the problematic code, making it much easier to identify and fix issues. + diff --git a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md index ef168b8..c1b0078 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md +++ b/src/EntityFrameworkCore.Projectables.Generator/AnalyzerReleases.Unshipped.md @@ -3,3 +3,6 @@ Rule ID | Category | Severity | Notes --------|----------|----------|-------------------- EFP0002 | Design | Error | +EFP0003 | Design | Warning | +EFP0004 | Design | Error | Statement with side effects in block-bodied method +EFP0005 | Design | Warning | Potential side effect in block-bodied method diff --git a/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs b/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs new file mode 100644 index 0000000..55abd92 --- /dev/null +++ b/src/EntityFrameworkCore.Projectables.Generator/BlockStatementConverter.cs @@ -0,0 +1,539 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace EntityFrameworkCore.Projectables.Generator +{ + /// + /// Converts block-bodied methods to expression syntax that can be used in expression trees. + /// Only supports a subset of C# statements. + /// + public class BlockStatementConverter + { + private readonly SourceProductionContext _context; + private readonly ExpressionSyntaxRewriter _expressionRewriter; + private readonly Dictionary _localVariables = new(); + + public BlockStatementConverter(SourceProductionContext context, ExpressionSyntaxRewriter expressionRewriter) + { + _context = context; + _expressionRewriter = expressionRewriter; + } + + /// + /// Attempts to convert a block statement into a single expression. + /// Returns null if the block contains unsupported statements. + /// + public ExpressionSyntax? TryConvertBlock(BlockSyntax block, string memberName) + { + if (block.Statements.Count == 0) + { + var diagnostic = Diagnostic.Create( + Diagnostics.UnsupportedStatementInBlockBody, + block.GetLocation(), + memberName, + "Block body must contain at least one statement" + ); + _context.ReportDiagnostic(diagnostic); + return null; + } + + // Try to convert the block statements into an expression + return TryConvertStatements(block.Statements.ToList(), memberName); + } + + private ExpressionSyntax? TryConvertStatements(List statements, string memberName) + { + if (statements.Count == 0) + { + return null; + } + + if (statements.Count == 1) + { + return TryConvertStatement(statements[0], memberName); + } + + // Multiple statements - try to convert them into a chain of expressions + // This is done by converting local variable declarations and then the final return + var nonReturnStatements = statements.Take(statements.Count - 1).ToList(); + var lastStatement = statements.Last(); + + // First, process any local variable declarations at the beginning + var localDeclStatements = new List(); + var remainingStatements = new List(); + + foreach (var stmt in nonReturnStatements) + { + if (stmt is LocalDeclarationStatementSyntax localDecl) + { + localDeclStatements.Add(localDecl); + } + else + { + remainingStatements.Add(stmt); + } + } + + // Process local variable declarations first + foreach (var localDecl in localDeclStatements) + { + if (!TryProcessLocalDeclaration(localDecl, memberName)) + { + return null; + } + } + + // Check if we have a pattern like multiple if statements without else followed by a final return: + // var x = ...; if (a) return 1; if (b) return 2; return 3; + // This can be converted to nested ternaries: a ? 1 : (b ? 2 : 3) + if (lastStatement is ReturnStatementSyntax finalReturn && + remainingStatements.All(s => s is IfStatementSyntax { Else: null })) + { + // All remaining non-return statements are if statements without else + var ifStatements = remainingStatements.Cast().ToList(); + + // Start with the final return as the base expression + var elseBody = TryConvertReturnStatement(finalReturn, memberName); + if (elseBody == null) + { + return null; + } + + // Build nested conditionals from right to left (last to first) + for (var i = ifStatements.Count - 1; i >= 0; i--) + { + var ifStmt = ifStatements[i]; + var ifBody = TryConvertStatement(ifStmt.Statement, memberName); + if (ifBody == null) + { + return null; + } + + // Rewrite the condition and replace any local variables + var condition = (ExpressionSyntax)_expressionRewriter.Visit(ifStmt.Condition); + condition = ReplaceLocalVariables(condition); + + elseBody = SyntaxFactory.ConditionalExpression(condition, ifBody, elseBody); + } + + return elseBody; + } + + // If there are any remaining non-if statements, try to convert them individually + // This will provide better error messages for unsupported statements + if (remainingStatements.Count > 0) + { + // Try converting each remaining statement - this will provide specific error messages + foreach (var stmt in remainingStatements) + { + var converted = TryConvertStatement(stmt, memberName); + if (converted == null) + { + return null; + } + } + + // If we got here but had non-if statements, they weren't properly handled + ReportUnsupportedStatement(remainingStatements[0], memberName, + "Only local variable declarations and if statements without else (with return) are supported before the final return statement"); + return null; + } + + // Convert the final statement (should be a return) + return TryConvertStatement(lastStatement, memberName); + } + + private bool TryProcessLocalDeclaration(LocalDeclarationStatementSyntax localDecl, string memberName) + { + foreach (var variable in localDecl.Declaration.Variables) + { + if (variable.Initializer == null) + { + ReportUnsupportedStatement(localDecl, memberName, "Local variables must have an initializer"); + return false; + } + + var variableName = variable.Identifier.Text; + + // Rewrite the initializer expression NOW while it's still in the tree + var rewrittenInitializer = (ExpressionSyntax)_expressionRewriter.Visit(variable.Initializer.Value); + + // Also expand any previously defined local variables in this initializer + // This ensures transitive inlining (e.g., var a = 1; var b = a + 2; return b; -> 1 + 2) + rewrittenInitializer = ReplaceLocalVariables(rewrittenInitializer); + + _localVariables[variableName] = rewrittenInitializer; + } + + return true; + } + + private ExpressionSyntax? TryConvertStatement(StatementSyntax statement, string memberName) + { + switch (statement) + { + case ReturnStatementSyntax returnStmt: + return TryConvertReturnStatement(returnStmt, memberName); + + case IfStatementSyntax ifStmt: + return TryConvertIfStatement(ifStmt, memberName); + + case SwitchStatementSyntax switchStmt: + return TryConvertSwitchStatement(switchStmt, memberName); + + case BlockSyntax blockStmt: + // Prevent locals declared in nested blocks from leaking into outer scopes + var nestedLocal = blockStmt.DescendantNodes() + .OfType() + .FirstOrDefault(); + + if (nestedLocal is not null) + { + ReportUnsupportedStatement(nestedLocal, memberName, "Local declarations in nested blocks are not supported"); + return null; + } + + return TryConvertStatements(blockStmt.Statements.ToList(), memberName); + + case ExpressionStatementSyntax exprStmt: + // Expression statements may contain side effects - analyze them + return AnalyzeExpressionStatement(exprStmt, memberName); + + case LocalDeclarationStatementSyntax: + // Local declarations should be handled before the return statement + ReportUnsupportedStatement(statement, memberName, "Local declarations must appear before the return statement"); + return null; + + default: + ReportUnsupportedStatement(statement, memberName, $"Statement type '{statement.GetType().Name}' is not supported"); + return null; + } + } + + private ExpressionSyntax? TryConvertReturnStatement(ReturnStatementSyntax returnStmt, string memberName) + { + if (returnStmt.Expression == null) + { + ReportUnsupportedStatement(returnStmt, memberName, "Return statement must have an expression"); + return null; + } + + // First rewrite the return expression + var expression = (ExpressionSyntax)_expressionRewriter.Visit(returnStmt.Expression); + + // Then replace any local variable references with their already-rewritten initializers + expression = ReplaceLocalVariables(expression); + + return expression; + } + + private ConditionalExpressionSyntax? TryConvertIfStatement(IfStatementSyntax ifStmt, string memberName) + { + // Convert if-else to conditional (ternary) expression + // First, rewrite the condition using the expression rewriter + var condition = (ExpressionSyntax)_expressionRewriter.Visit(ifStmt.Condition); + + // Then replace any local variable references with their already-rewritten initializers + condition = ReplaceLocalVariables(condition); + + var whenTrue = TryConvertStatement(ifStmt.Statement, memberName); + if (whenTrue == null) + { + return null; + } + + ExpressionSyntax? whenFalse; + if (ifStmt.Else != null) + { + whenFalse = TryConvertStatement(ifStmt.Else.Statement, memberName); + if (whenFalse == null) + { + return null; + } + } + else + { + // If there's no else clause, use a default literal + // This will be inferred to the correct type by the compiler + whenFalse = SyntaxFactory.LiteralExpression( + SyntaxKind.DefaultLiteralExpression, + SyntaxFactory.Token(SyntaxKind.DefaultKeyword) + ); + } + + // Create a conditional expression with the rewritten nodes + return SyntaxFactory.ConditionalExpression( + condition, + whenTrue, + whenFalse + ); + } + + private ExpressionSyntax? TryConvertSwitchStatement(SwitchStatementSyntax switchStmt, string memberName) + { + // Convert switch statement to nested conditional expressions + // Process sections in reverse order to build from the default case up + + var switchExpression = (ExpressionSyntax)_expressionRewriter.Visit(switchStmt.Expression); + // Replace any local variable references in the switch expression + switchExpression = ReplaceLocalVariables(switchExpression); + + ExpressionSyntax? currentExpression; + + // Find default case first + SwitchSectionSyntax? defaultSection = null; + var nonDefaultSections = new List(); + + foreach (var section in switchStmt.Sections) + { + var hasDefault = section.Labels.Any(label => label is DefaultSwitchLabelSyntax); + if (hasDefault) + { + defaultSection = section; + } + else + { + nonDefaultSections.Add(section); + } + } + + // Start with default case or null + if (defaultSection != null) + { + currentExpression = ConvertSwitchSection(defaultSection, memberName); + if (currentExpression == null) + { + return null; + } + } + else + { + // No default case - use default literal + currentExpression = SyntaxFactory.LiteralExpression( + SyntaxKind.DefaultLiteralExpression, + SyntaxFactory.Token(SyntaxKind.DefaultKeyword) + ); + } + + // Process non-default sections in reverse order + for (var i = nonDefaultSections.Count - 1; i >= 0; i--) + { + var section = nonDefaultSections[i]; + var sectionExpression = ConvertSwitchSection(section, memberName); + if (sectionExpression == null) + { + return null; + } + + // Build condition for all labels in this section (OR'd together) + ExpressionSyntax? condition = null; + foreach (var label in section.Labels) + { + if (label is CaseSwitchLabelSyntax caseLabel) + { + // Rewrite and replace locals in case label value + var caseLabelValue = (ExpressionSyntax)_expressionRewriter.Visit(caseLabel.Value); + caseLabelValue = ReplaceLocalVariables(caseLabelValue); + + var labelCondition = SyntaxFactory.BinaryExpression( + SyntaxKind.EqualsExpression, + switchExpression, + caseLabelValue + ); + + condition = condition == null + ? labelCondition + : SyntaxFactory.BinaryExpression( + SyntaxKind.LogicalOrExpression, + condition, + labelCondition + ); + } + else if (label is not DefaultSwitchLabelSyntax) + { + // Unsupported label type (e.g., pattern-based switch in older syntax) + ReportUnsupportedStatement(switchStmt, memberName, + $"Switch label type '{label.GetType().Name}' is not supported. Use case labels or switch expressions instead."); + return null; + } + } + + if (condition != null) + { + currentExpression = SyntaxFactory.ConditionalExpression( + condition, + sectionExpression, + currentExpression + ); + } + } + + return currentExpression; + } + + private ExpressionSyntax? ConvertSwitchSection(SwitchSectionSyntax section, string memberName) + { + // Convert the statements in the switch section + // Most switch sections end with break, return, or throw + var statements = section.Statements.ToList(); + + // Remove trailing break statements as they're not needed in expressions + if (statements.Count > 0 && statements.Last() is BreakStatementSyntax) + { + statements = statements.Take(statements.Count - 1).ToList(); + } + + if (statements.Count != 0) + { + return TryConvertStatements(statements, memberName); + } + + // Use the section's first label location for error reporting + var firstLabel = section.Labels.FirstOrDefault(); + if (firstLabel == null) + { + return null; + } + + var diagnostic = Diagnostic.Create( + Diagnostics.UnsupportedStatementInBlockBody, + firstLabel.GetLocation(), + memberName, + "Switch section must have at least one statement" + ); + _context.ReportDiagnostic(diagnostic); + return null; + + } + + private ExpressionSyntax ReplaceLocalVariables(ExpressionSyntax expression) + { + // Use a rewriter to replace local variable references with their initializer expressions + var rewriter = new LocalVariableReplacer(_localVariables); + return (ExpressionSyntax)rewriter.Visit(expression); + } + + private ExpressionSyntax? AnalyzeExpressionStatement(ExpressionStatementSyntax exprStmt, string memberName) + { + var expression = exprStmt.Expression; + + // Check for specific side effects + switch (expression) + { + case AssignmentExpressionSyntax assignment: + ReportSideEffect(assignment, GetAssignmentErrorMessage(assignment)); + return null; + + case PostfixUnaryExpressionSyntax postfix when + postfix.IsKind(SyntaxKind.PostIncrementExpression) || + postfix.IsKind(SyntaxKind.PostDecrementExpression): + ReportSideEffect(postfix, $"Increment/decrement operator '{postfix.OperatorToken.Text}' has side effects and cannot be used in projectable methods"); + return null; + + case PrefixUnaryExpressionSyntax prefix when + prefix.IsKind(SyntaxKind.PreIncrementExpression) || + prefix.IsKind(SyntaxKind.PreDecrementExpression): + ReportSideEffect(prefix, $"Increment/decrement operator '{prefix.OperatorToken.Text}' has side effects and cannot be used in projectable methods"); + return null; + + case InvocationExpressionSyntax invocation: + // Check if this is a potentially impure method call + var symbolInfo = _expressionRewriter.GetSemanticModel().GetSymbolInfo(invocation); + if (symbolInfo.Symbol is IMethodSymbol methodSymbol) + { + // Check if method has [Projectable] attribute - those are safe + var hasProjectableAttr = methodSymbol.GetAttributes() + .Any(attr => attr.AttributeClass?.Name == "ProjectableAttribute"); + + if (!hasProjectableAttr) + { + ReportPotentialSideEffect(invocation, + $"Method call '{methodSymbol.Name}' may have side effects. Only calls to methods marked with [Projectable] are guaranteed to be safe in projectable methods"); + return null; + } + } + break; + } + + // If we got here, it's an expression statement we don't support + ReportUnsupportedStatement(exprStmt, memberName, "Expression statements are not supported in projectable methods"); + return null; + } + + private string GetAssignmentErrorMessage(AssignmentExpressionSyntax assignment) + { + var operatorText = assignment.OperatorToken.Text; + + if (assignment.IsKind(SyntaxKind.SimpleAssignmentExpression)) + { + if (assignment.Left is MemberAccessExpressionSyntax memberAccess) + { + return $"Property assignment '{memberAccess.Name}' has side effects and cannot be used in projectable methods"; + } + return $"Assignment operation has side effects and cannot be used in projectable methods"; + } + else + { + // Compound assignment like +=, -=, etc. + return $"Compound assignment operator '{operatorText}' has side effects and cannot be used in projectable methods"; + } + } + + private void ReportSideEffect(SyntaxNode node, string message) + { + var diagnostic = Diagnostic.Create( + Diagnostics.SideEffectInBlockBody, + node.GetLocation(), + message + ); + _context.ReportDiagnostic(diagnostic); + } + + private void ReportPotentialSideEffect(SyntaxNode node, string message) + { + var diagnostic = Diagnostic.Create( + Diagnostics.PotentialSideEffectInBlockBody, + node.GetLocation(), + message + ); + _context.ReportDiagnostic(diagnostic); + } + + private void ReportUnsupportedStatement(StatementSyntax statement, string memberName, string reason) + { + var diagnostic = Diagnostic.Create( + Diagnostics.UnsupportedStatementInBlockBody, + statement.GetLocation(), + memberName, + reason + ); + _context.ReportDiagnostic(diagnostic); + } + + + private class LocalVariableReplacer : CSharpSyntaxRewriter + { + private readonly Dictionary _localVariables; + + public LocalVariableReplacer(Dictionary localVariables) + { + _localVariables = localVariables; + } + + public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node) + { + var identifier = node.Identifier.Text; + if (_localVariables.TryGetValue(identifier, out var replacement)) + { + // Replace the identifier with the expression it was initialized with + return replacement.WithTriviaFrom(node); + } + + return base.VisitIdentifierName(node); + } + } + } +} diff --git a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs index 18f87c1..6bcfaf1 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/Diagnostics.cs @@ -25,5 +25,29 @@ public static class Diagnostics DiagnosticSeverity.Error, isEnabledByDefault: true); + public static readonly DiagnosticDescriptor UnsupportedStatementInBlockBody = new DiagnosticDescriptor( + id: "EFP0003", + title: "Unsupported statement in block-bodied method", + messageFormat: "Method '{0}' contains an unsupported statement: {1}", + category: "Design", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor SideEffectInBlockBody = new DiagnosticDescriptor( + id: "EFP0004", + title: "Statement with side effects in block-bodied method", + messageFormat: "{0}", + category: "Design", + DiagnosticSeverity.Error, + isEnabledByDefault: true); + + public static readonly DiagnosticDescriptor PotentialSideEffectInBlockBody = new DiagnosticDescriptor( + id: "EFP0005", + title: "Potential side effect in block-bodied method", + messageFormat: "{0}", + category: "Design", + DiagnosticSeverity.Warning, + isEnabledByDefault: true); + } } diff --git a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs index f51c8f6..f953701 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/ExpressionSyntaxRewriter.cs @@ -1,4 +1,4 @@ -using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; @@ -26,6 +26,8 @@ public ExpressionSyntaxRewriter(INamedTypeSymbol targetTypeSymbol, NullCondition _extensionParameterName = extensionParameterName; } + public SemanticModel GetSemanticModel() => _semanticModel; + private SyntaxNode? VisitThisBaseExpression(CSharpSyntaxNode node) { // Swap out the use of this and base to @this and keep leading and trailing trivias @@ -386,8 +388,47 @@ private ExpressionSyntax CreateMethodCallOnEnumValue(IMethodSymbol methodSymbol, continue; } + // Handle relational patterns (<=, <, >=, >) + if (arm.Pattern is RelationalPatternSyntax relational) + { + // Map the pattern operator token to a binary expression kind + var binaryKind = relational.OperatorToken.Kind() switch + { + SyntaxKind.LessThanToken => SyntaxKind.LessThanExpression, + SyntaxKind.LessThanEqualsToken => SyntaxKind.LessThanOrEqualExpression, + SyntaxKind.GreaterThanToken => SyntaxKind.GreaterThanExpression, + SyntaxKind.GreaterThanEqualsToken => SyntaxKind.GreaterThanOrEqualExpression, + _ => throw new InvalidOperationException( + $"Unsupported relational operator in switch expression: {relational.OperatorToken.Kind()}") + }; + + var condition = SyntaxFactory.BinaryExpression( + binaryKind, + (ExpressionSyntax)Visit(node.GoverningExpression), + (ExpressionSyntax)Visit(relational.Expression) + ); + + // Add the when clause as a AND expression + if (arm.WhenClause != null) + { + condition = SyntaxFactory.BinaryExpression( + SyntaxKind.LogicalAndExpression, + condition, + (ExpressionSyntax)Visit(arm.WhenClause.Condition) + ); + } + + currentExpression = SyntaxFactory.ConditionalExpression( + condition, + armExpression, + currentExpression + ); + + continue; + } + throw new InvalidOperationException( - $"Switch expressions rewriting supports only constant values and declaration patterns (Type var). " + + $"Switch expressions rewriting supports constant values, relational patterns (<=, <, >=, >), and declaration patterns (Type var). " + $"Unsupported pattern: {arm.Pattern.GetType().Name}" ); } diff --git a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs index 8a84310..5006081 100644 --- a/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs +++ b/src/EntityFrameworkCore.Projectables.Generator/ProjectableInterpreter.cs @@ -120,7 +120,7 @@ x is IPropertySymbol xProperty && return false; } else if (x is MethodDeclarationSyntax xMethod && - xMethod.ExpressionBody is not null) + (xMethod.ExpressionBody is not null || xMethod.Body is not null)) { return true; } @@ -302,7 +302,28 @@ x is IPropertySymbol xProperty && if (memberBody is MethodDeclarationSyntax methodDeclarationSyntax) { - if (methodDeclarationSyntax.ExpressionBody is null) + ExpressionSyntax? bodyExpression = null; + + if (methodDeclarationSyntax.ExpressionBody is not null) + { + // Expression-bodied method (e.g., int Foo() => 1;) + bodyExpression = methodDeclarationSyntax.ExpressionBody.Expression; + } + else if (methodDeclarationSyntax.Body is not null) + { + // Block-bodied method (e.g., int Foo() { return 1; }) + var blockConverter = new BlockStatementConverter(context, expressionSyntaxRewriter); + bodyExpression = blockConverter.TryConvertBlock(methodDeclarationSyntax.Body, memberSymbol.Name); + + if (bodyExpression is null) + { + // Diagnostics already reported by BlockStatementConverter + return null; + } + + // The expression has already been rewritten by BlockStatementConverter, so we don't rewrite it again + } + else { var diagnostic = Diagnostic.Create(Diagnostics.RequiresExpressionBodyDefinition, methodDeclarationSyntax.GetLocation(), memberSymbol.Name); context.ReportDiagnostic(diagnostic); @@ -312,7 +333,10 @@ x is IPropertySymbol xProperty && var returnType = declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ReturnType); descriptor.ReturnTypeName = returnType.ToString(); - descriptor.ExpressionBody = (ExpressionSyntax)expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression); + // Only rewrite expression-bodied methods, block-bodied methods are already rewritten + descriptor.ExpressionBody = methodDeclarationSyntax.ExpressionBody is not null + ? (ExpressionSyntax)expressionSyntaxRewriter.Visit(bodyExpression) + : bodyExpression; foreach (var additionalParameter in ((ParameterListSyntax)declarationSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList)).Parameters) { descriptor.ParametersList = descriptor.ParametersList.AddParameters(additionalParameter); diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..3eaf767 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..3eaf767 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt new file mode 100644 index 0000000..3eaf767 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ArithmeticInReturn_WorksCorrectly.verified.txt @@ -0,0 +1,2 @@ +SELECT (CAST([e].[Value] AS float) / 100.0E0) * 50.0E0 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..dab6bd4 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT 15 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..dab6bd4 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT 15 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt new file mode 100644 index 0000000..dab6bd4 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BlockMethodWithParameters_WorksCorrectly.verified.txt @@ -0,0 +1,2 @@ +SELECT 15 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..e5b6efb --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..e5b6efb --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt new file mode 100644 index 0000000..e5b6efb --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.BooleanReturn_WorksCorrectly.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..a19f725 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2 + ELSE 0 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..a19f725 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2 + ELSE 0 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt new file mode 100644 index 0000000..a19f725 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ComplexConditional_IsTranslatedCorrectly.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 0 THEN [e].[Value] * 2 + ELSE 0 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..ba1f2c1 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet10_0.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/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..ba1f2c1 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.DotNet9_0.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/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.verified.txt new file mode 100644 index 0000000..ba1f2c1 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalAccess_WorksCorrectly.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/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..4d0592a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active' + ELSE N'Active' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..4d0592a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active' + ELSE N'Active' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt new file mode 100644 index 0000000..4d0592a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ConditionalWithNegation_WorksCorrectly.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN N'Not Active' + ELSE N'Active' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt new file mode 100644 index 0000000..a29be77 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet10_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0 + WHEN [e].[Value] < 0 THEN 0 + ELSE [e].[Value] * 2 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt new file mode 100644 index 0000000..a29be77 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.DotNet9_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0 + WHEN [e].[Value] < 0 THEN 0 + ELSE [e].[Value] * 2 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt new file mode 100644 index 0000000..a29be77 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.GuardClause_WithEarlyReturn.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(0 AS bit) THEN 0 + WHEN [e].[Value] < 0 THEN 0 + ELSE [e].[Value] * 2 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt new file mode 100644 index 0000000..26ac26b --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt new file mode 100644 index 0000000..26ac26b --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt new file mode 100644 index 0000000..26ac26b --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfElseStatement_IsTranslatedToTernary.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt new file mode 100644 index 0000000..0c5fe1e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet10_0.verified.txt @@ -0,0 +1,4 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt new file mode 100644 index 0000000..0c5fe1e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.DotNet9_0.verified.txt @@ -0,0 +1,4 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2 +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt new file mode 100644 index 0000000..7e3c8c6 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_UsesDefault.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN [e].[Value] * 2 + ELSE NULL +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.IfWithoutElse_WithFallbackReturn.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt new file mode 100644 index 0000000..eec38d9 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt new file mode 100644 index 0000000..eec38d9 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt new file mode 100644 index 0000000..eec38d9 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariableReuse_IsInlinedMultipleTimes.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt new file mode 100644 index 0000000..9689484 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 5 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt new file mode 100644 index 0000000..9689484 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 5 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt new file mode 100644 index 0000000..9689484 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.LocalVariable_IsInlined.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 5 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt new file mode 100644 index 0000000..257f6f0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet10_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'Active High' + WHEN [e].[Value] > 50 THEN N'Active Medium' + ELSE N'Active Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt new file mode 100644 index 0000000..257f6f0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.DotNet9_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'Active High' + WHEN [e].[Value] > 50 THEN N'Active Medium' + ELSE N'Active Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt new file mode 100644 index 0000000..257f6f0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MixedIfAndSwitch_WithMultiplePatterns.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'Active High' + WHEN [e].[Value] > 50 THEN N'Active Medium' + ELSE N'Active Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt new file mode 100644 index 0000000..1ae6355 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Very High' + WHEN [e].[Value] > 50 THEN N'High' + WHEN [e].[Value] > 10 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt new file mode 100644 index 0000000..1ae6355 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Very High' + WHEN [e].[Value] > 50 THEN N'High' + WHEN [e].[Value] > 10 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt new file mode 100644 index 0000000..1ae6355 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleEarlyReturns_ConvertedToNestedTernaries.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Very High' + WHEN [e].[Value] > 50 THEN N'High' + WHEN [e].[Value] > 10 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..4a903b0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..4a903b0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt new file mode 100644 index 0000000..4a903b0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.MultipleLocalVariables_AreInlinedCorrectly.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + [e].[Value] * 3 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt new file mode 100644 index 0000000..6973619 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High' + WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium' + WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt new file mode 100644 index 0000000..6973619 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High' + WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium' + WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt new file mode 100644 index 0000000..6973619 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedConditionals_WithLogicalOperators.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100 THEN N'Active High' + WHEN [e].[IsActive] = CAST(1 AS bit) OR [e].[Value] > 50 THEN N'Active or Medium' + WHEN [e].[IsActive] = CAST(0 AS bit) AND [e].[Value] <= 10 THEN N'Inactive Low' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet10_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.DotNet9_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedIfElse_IsTranslatedToNestedTernary.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..5f5a209 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] = 1 THEN N'Active One' + WHEN [e].[Value] = 2 THEN N'Active Two' + ELSE N'Active Other' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..5f5a209 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] = 1 THEN N'Active One' + WHEN [e].[Value] = 2 THEN N'Active Two' + ELSE N'Active Other' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt new file mode 100644 index 0000000..5f5a209 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedSwitchInIf_WorksCorrectly.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] = 1 THEN N'Active One' + WHEN [e].[Value] = 2 THEN N'Active Two' + ELSE N'Active Other' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt new file mode 100644 index 0000000..9d42002 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NestedTernary_WorksCorrectly.verified.txt @@ -0,0 +1,6 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'High' + WHEN [e].[Value] > 50 THEN N'Medium' + ELSE N'Low' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..52f2a3e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT COALESCE([e].[Name], N'Unknown') +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..52f2a3e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT COALESCE([e].[Name], N'Unknown') +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt new file mode 100644 index 0000000..52f2a3e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.NullCoalescing_WorksCorrectly.verified.txt @@ -0,0 +1,2 @@ +SELECT COALESCE([e].[Name], N'Unknown') +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt new file mode 100644 index 0000000..06a56fa --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt new file mode 100644 index 0000000..06a56fa --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt new file mode 100644 index 0000000..06a56fa --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.ReturnWithPropertyAccess_IsTranslatedToSql.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt new file mode 100644 index 0000000..6efc8d2 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt new file mode 100644 index 0000000..6efc8d2 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt new file mode 100644 index 0000000..6efc8d2 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SimpleReturn_IsTranslatedToSql.verified.txt @@ -0,0 +1,2 @@ +SELECT 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..e6bf43e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..e6bf43e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt new file mode 100644 index 0000000..e6bf43e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.StringInterpolation_WorksCorrectly.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_Simple.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt new file mode 100644 index 0000000..727148f --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] <= 2 THEN N'Low' + WHEN [e].[Value] <= 5 THEN N'Medium' + WHEN [e].[Value] <= 8 THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt new file mode 100644 index 0000000..727148f --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] <= 2 THEN N'Low' + WHEN [e].[Value] <= 5 THEN N'Medium' + WHEN [e].[Value] <= 8 THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt new file mode 100644 index 0000000..727148f --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchExpression_WithDiscard.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] <= 2 THEN N'Low' + WHEN [e].[Value] <= 5 THEN N'Medium' + WHEN [e].[Value] <= 8 THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt new file mode 100644 index 0000000..9ed7fa8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_Simple.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 THEN N'One' + WHEN [e].[Value] = 2 THEN N'Two' + WHEN [e].[Value] = 3 THEN N'Three' + ELSE N'Many' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt new file mode 100644 index 0000000..9c8b78e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] IN (1, 2) THEN N'Low' + WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium' + WHEN [e].[Value] IN (6, 7, 8) THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt new file mode 100644 index 0000000..9c8b78e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] IN (1, 2) THEN N'Low' + WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium' + WHEN [e].[Value] IN (6, 7, 8) THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt new file mode 100644 index 0000000..9c8b78e --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchStatement_WithMultipleCases.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] IN (1, 2) THEN N'Low' + WHEN [e].[Value] IN (3, 4, 5) THEN N'Medium' + WHEN [e].[Value] IN (6, 7, 8) THEN N'High' + ELSE N'Critical' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..f2343d3 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One' + WHEN [e].[Value] = 1 THEN N'Inactive One' + WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..f2343d3 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One' + WHEN [e].[Value] = 1 THEN N'Inactive One' + WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt new file mode 100644 index 0000000..f2343d3 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.SwitchWithWhenClause_WorksCorrectly.verified.txt @@ -0,0 +1,7 @@ +SELECT CASE + WHEN [e].[Value] = 1 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active One' + WHEN [e].[Value] = 1 THEN N'Inactive One' + WHEN [e].[Value] > 10 AND [e].[IsActive] = CAST(1 AS bit) THEN N'Active High' + ELSE N'Other' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt new file mode 100644 index 0000000..f3f5c43 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.TernaryExpression_WorksCorrectly.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN N'Active' + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs new file mode 100644 index 0000000..9622f34 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodiedMethodTests.cs @@ -0,0 +1,672 @@ +using EntityFrameworkCore.Projectables.FunctionalTests.Helpers; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Threading.Tasks; +using VerifyXunit; +using Xunit; + +namespace EntityFrameworkCore.Projectables.FunctionalTests.BlockBodiedMethods +{ + [UsesVerify] + public class BlockBodiedMethodTests + { + public record Entity + { + public int Id { get; set; } + public int Value { get; set; } + public bool IsActive { get; set; } + public string? Name { get; set; } + } + + [Fact] + public Task SimpleReturn_IsTranslatedToSql() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetConstant()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task ReturnWithPropertyAccess_IsTranslatedToSql() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValuePlusTen()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task IfElseStatement_IsTranslatedToTernary() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetCategory()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task NestedIfElse_IsTranslatedToNestedTernary() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetLevel()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task LocalVariable_IsInlined() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.CalculateDouble()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task ComplexConditional_IsTranslatedCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetAdjustedValue()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockMethodWithParameters_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.Add(5, 10)); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task IfWithoutElse_UsesDefault() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetPremiumIfActive()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task IfWithoutElse_WithFallbackReturn() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetStatus()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task SwitchStatement_Simple() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValueLabel()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task SwitchStatement_WithMultipleCases() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetPriority()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task MultipleEarlyReturns_ConvertedToNestedTernaries() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValueCategory()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task NullCoalescing_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetNameOrDefault()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task ConditionalAccess_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetNameLength()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task SwitchExpression_Simple() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValueLabelModern()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task SwitchExpression_WithDiscard() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetPriorityModern()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task MultipleLocalVariables_AreInlinedCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.CalculateComplex()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task NestedConditionals_WithLogicalOperators() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetComplexCategory()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task GuardClause_WithEarlyReturn() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetGuardedValue()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task NestedSwitchInIf_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetCombinedLogic()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task TernaryExpression_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValueUsingTernary()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task NestedTernary_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetNestedTernary()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task MixedIfAndSwitch_WithMultiplePatterns() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetComplexMix()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task SwitchWithWhenClause_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetValueWithCondition()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task LocalVariableReuse_IsInlinedMultipleTimes() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.CalculateWithReuse()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BooleanReturn_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.IsHighValue()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task ConditionalWithNegation_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetInactiveStatus()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task StringInterpolation_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.GetFormattedValue()); + + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task ArithmeticInReturn_WorksCorrectly() + { + using var dbContext = new SampleDbContext(); + + var query = dbContext.Set() + .Select(x => x.CalculatePercentage()); + + return Verifier.Verify(query.ToQueryString()); + } + } + + public static class EntityExtensions + { + [Projectable] + public static int GetConstant(this BlockBodiedMethodTests.Entity entity) + { + return 42; + } + + [Projectable] + public static int GetValuePlusTen(this BlockBodiedMethodTests.Entity entity) + { + return entity.Value + 10; + } + + [Projectable] + public static string GetCategory(this BlockBodiedMethodTests.Entity entity) + { + if (entity.Value > 100) + { + return "High"; + } + else + { + return "Low"; + } + } + + [Projectable] + public static string GetLevel(this BlockBodiedMethodTests.Entity entity) + { + if (entity.Value > 100) + { + return "High"; + } + else if (entity.Value > 50) + { + return "Medium"; + } + else + { + return "Low"; + } + } + + [Projectable] + public static int CalculateDouble(this BlockBodiedMethodTests.Entity entity) + { + var doubled = entity.Value * 2; + return doubled + 5; + } + + [Projectable] + public static int GetAdjustedValue(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive && entity.Value > 0) + { + return entity.Value * 2; + } + else + { + return 0; + } + } + + [Projectable] + public static int Add(this BlockBodiedMethodTests.Entity entity, int a, int b) + { + return a + b; + } + + [Projectable] + public static int? GetPremiumIfActive(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive) + { + return entity.Value * 2; + } + return null; + } + + [Projectable] + public static string GetStatus(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive) + { + return "Active"; + } + return "Inactive"; + } + + [Projectable] + public static string GetValueLabel(this BlockBodiedMethodTests.Entity entity) + { + switch (entity.Value) + { + case 1: + return "One"; + case 2: + return "Two"; + case 3: + return "Three"; + default: + return "Many"; + } + } + + [Projectable] + public static string GetPriority(this BlockBodiedMethodTests.Entity entity) + { + switch (entity.Value) + { + case 1: + case 2: + return "Low"; + case 3: + case 4: + case 5: + return "Medium"; + case 6: + case 7: + case 8: + return "High"; + default: + return "Critical"; + } + } + + [Projectable] + public static string GetValueCategory(this BlockBodiedMethodTests.Entity entity) + { + if (entity.Value > 100) + { + return "Very High"; + } + + if (entity.Value > 50) + { + return "High"; + } + + if (entity.Value > 10) + { + return "Medium"; + } + + return "Low"; + } + + [Projectable] + public static string GetNameOrDefault(this BlockBodiedMethodTests.Entity entity) + { + return entity.Name ?? "Unknown"; + } + + [Projectable(NullConditionalRewriteSupport = NullConditionalRewriteSupport.Rewrite)] + public static int? GetNameLength(this BlockBodiedMethodTests.Entity entity) + { + return entity.Name?.Length; + } + + [Projectable] + public static string GetValueLabelModern(this BlockBodiedMethodTests.Entity entity) + { + return entity.Value switch + { + 1 => "One", + 2 => "Two", + 3 => "Three", + _ => "Many" + }; + } + + [Projectable] + public static string GetPriorityModern(this BlockBodiedMethodTests.Entity entity) + { + return entity.Value switch + { + <= 2 => "Low", + <= 5 => "Medium", + <= 8 => "High", + _ => "Critical" + }; + } + + [Projectable] + public static int CalculateComplex(this BlockBodiedMethodTests.Entity entity) + { + var doubled = entity.Value * 2; + var tripled = entity.Value * 3; + var sum = doubled + tripled; + return sum + 10; + } + + [Projectable] + public static string GetComplexCategory(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive && entity.Value > 100) + { + return "Active High"; + } + + if (entity.IsActive || entity.Value > 50) + { + return "Active or Medium"; + } + + if (!entity.IsActive && entity.Value <= 10) + { + return "Inactive Low"; + } + + return "Other"; + } + + [Projectable] + public static int GetGuardedValue(this BlockBodiedMethodTests.Entity entity) + { + if (!entity.IsActive) + { + return 0; + } + + if (entity.Value < 0) + { + return 0; + } + + return entity.Value * 2; + } + + [Projectable] + public static string GetCombinedLogic(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive) + { + switch (entity.Value) + { + case 1: + return "Active One"; + case 2: + return "Active Two"; + default: + return "Active Other"; + } + } + + return "Inactive"; + } + + [Projectable] + public static string GetValueUsingTernary(this BlockBodiedMethodTests.Entity entity) + { + return entity.IsActive ? "Active" : "Inactive"; + } + + [Projectable] + public static string GetNestedTernary(this BlockBodiedMethodTests.Entity entity) + { + return entity.Value > 100 ? "High" : entity.Value > 50 ? "Medium" : "Low"; + } + + [Projectable] + public static string GetComplexMix(this BlockBodiedMethodTests.Entity entity) + { + if (entity.IsActive) + { + return entity.Value switch + { + > 100 => "Active High", + > 50 => "Active Medium", + _ => "Active Low" + }; + } + + return "Inactive"; + } + + [Projectable] + public static string GetValueWithCondition(this BlockBodiedMethodTests.Entity entity) + { + return entity.Value switch + { + 1 when entity.IsActive => "Active One", + 1 => "Inactive One", + > 10 when entity.IsActive => "Active High", + _ => "Other" + }; + } + + [Projectable] + public static int CalculateWithReuse(this BlockBodiedMethodTests.Entity entity) + { + var doubled = entity.Value * 2; + return doubled + doubled; + } + + [Projectable] + public static bool IsHighValue(this BlockBodiedMethodTests.Entity entity) + { + if (entity.Value > 100) + { + return true; + } + return false; + } + + [Projectable] + public static string GetInactiveStatus(this BlockBodiedMethodTests.Entity entity) + { + if (!entity.IsActive) + { + return "Not Active"; + } + else + { + return "Active"; + } + } + + [Projectable] + public static string GetFormattedValue(this BlockBodiedMethodTests.Entity entity) + { + return $"Value: {entity.Value}"; + } + + [Projectable] + public static double CalculatePercentage(this BlockBodiedMethodTests.Entity entity) + { + return (double)entity.Value / 100.0 * 50.0; + } + } +} diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs new file mode 100644 index 0000000..c09237b --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTest.cs @@ -0,0 +1,273 @@ +using EntityFrameworkCore.Projectables.FunctionalTests.Helpers; +using Microsoft.EntityFrameworkCore; +using System.Linq; +using System.Threading.Tasks; +using VerifyXunit; +using Xunit; + +namespace EntityFrameworkCore.Projectables.FunctionalTests.BlockBodiedMethods +{ + /// + /// Tests for calling projectable methods from within block-bodied methods + /// + [UsesVerify] + public class BlockBodyProjectableCallTests + { + public record Entity + { + public int Id { get; set; } + public int Value { get; set; } + public bool IsActive { get; set; } + public string? Name { get; set; } + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_Simple() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetAdjustedWithConstant()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InReturn() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetDoubledValue()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InCondition() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetCategoryBasedOnAdjusted()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_Multiple() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.CombineProjectableMethods()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InSwitch() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetLabelBasedOnCategory()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InSwitchExpression() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetDescriptionByLevel()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_WithLocalVariable() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.CalculateUsingProjectable()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_Nested() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetNestedProjectableCall()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InEarlyReturn() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetStatusWithProjectableCheck()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InTernary() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.GetConditionalProjectable()); + return Verifier.Verify(query.ToQueryString()); + } + + [Fact] + public Task BlockBodyCallingProjectableMethod_InLogicalExpression() + { + using var dbContext = new SampleDbContext(); + var query = dbContext.Set() + .Select(x => x.IsComplexCondition()); + return Verifier.Verify(query.ToQueryString()); + } + } + + public static class ProjectableCallExtensions + { + // Base projectable methods (helper methods) + [Projectable] + public static int GetConstant(this BlockBodyProjectableCallTests.Entity entity) + { + return 42; + } + + [Projectable] + public static int GetDoubled(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.Value * 2; + } + + [Projectable] + public static string GetCategory(this BlockBodyProjectableCallTests.Entity entity) + { + if (entity.Value > 100) + return "High"; + else + return "Low"; + } + + [Projectable] + public static string GetLevel(this BlockBodyProjectableCallTests.Entity entity) + { + if (entity.Value > 100) return "Level3"; + if (entity.Value > 50) return "Level2"; + return "Level1"; + } + + [Projectable] + public static bool IsHighValue(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.Value > 100; + } + + // Block-bodied methods calling projectable methods + + [Projectable] + public static int GetAdjustedWithConstant(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.Value + entity.GetConstant(); + } + + [Projectable] + public static int GetDoubledValue(this BlockBodyProjectableCallTests.Entity entity) + { + var doubled = entity.GetDoubled(); + return doubled; + } + + [Projectable] + public static string GetCategoryBasedOnAdjusted(this BlockBodyProjectableCallTests.Entity entity) + { + if (entity.GetDoubled() > 200) + { + return "Very High"; + } + else + { + return "Normal"; + } + } + + [Projectable] + public static int CombineProjectableMethods(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.GetDoubled() + entity.GetConstant(); + } + + [Projectable] + public static string GetLabelBasedOnCategory(this BlockBodyProjectableCallTests.Entity entity) + { + switch (entity.GetCategory()) + { + case "High": + return "Premium"; + case "Low": + return "Standard"; + default: + return "Unknown"; + } + } + + [Projectable] + public static string GetDescriptionByLevel(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.GetLevel() switch + { + "Level3" => "Expert", + "Level2" => "Intermediate", + "Level1" => "Beginner", + _ => "Unknown" + }; + } + + [Projectable] + public static int CalculateUsingProjectable(this BlockBodyProjectableCallTests.Entity entity) + { + var doubled = entity.GetDoubled(); + var withConstant = doubled + entity.GetConstant(); + return withConstant * 2; + } + + [Projectable] + public static int GetNestedProjectableCall(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.GetAdjustedWithConstant() + 10; + } + + [Projectable] + public static string GetStatusWithProjectableCheck(this BlockBodyProjectableCallTests.Entity entity) + { + if (entity.IsHighValue()) + return "Premium"; + + if (entity.GetCategory() == "High") + return "Standard High"; + + return "Normal"; + } + + [Projectable] + public static string GetConditionalProjectable(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.IsActive ? entity.GetCategory() : "Inactive"; + } + + [Projectable] + public static string GetChainedResult(this BlockBodyProjectableCallTests.Entity entity) + { + var doubled = entity.GetDoubled(); + + if (doubled > 200) + { + return entity.GetCategory() + " Priority"; + } + + return entity.GetLevel(); + } + + [Projectable] + public static bool IsComplexCondition(this BlockBodyProjectableCallTests.Entity entity) + { + return entity.IsActive && entity.IsHighValue() || entity.GetDoubled() > 150; + } + } +} diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt new file mode 100644 index 0000000..478d0ba --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] * 2 > 200 THEN N'Very High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt new file mode 100644 index 0000000..478d0ba --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] * 2 > 200 THEN N'Very High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt new file mode 100644 index 0000000..478d0ba --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InCondition.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN [e].[Value] * 2 > 200 THEN N'Very High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt new file mode 100644 index 0000000..bd650a0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet10_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Standard High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt new file mode 100644 index 0000000..bd650a0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.DotNet9_0.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Standard High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt new file mode 100644 index 0000000..bd650a0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InEarlyReturn.verified.txt @@ -0,0 +1,9 @@ +SELECT CASE + WHEN [e].[Value] > 100 THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Standard High' + ELSE N'Normal' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt new file mode 100644 index 0000000..de3373a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet10_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt new file mode 100644 index 0000000..de3373a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.DotNet9_0.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt new file mode 100644 index 0000000..de3373a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InLogicalExpression.verified.txt @@ -0,0 +1,5 @@ +SELECT CASE + WHEN ([e].[IsActive] = CAST(1 AS bit) AND [e].[Value] > 100) OR [e].[Value] * 2 > 150 THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt new file mode 100644 index 0000000..dea1914 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt new file mode 100644 index 0000000..dea1914 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt new file mode 100644 index 0000000..dea1914 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InReturn.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt new file mode 100644 index 0000000..927c6ff --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet10_0.verified.txt @@ -0,0 +1,12 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'Low' THEN N'Standard' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt new file mode 100644 index 0000000..927c6ff --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.DotNet9_0.verified.txt @@ -0,0 +1,12 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'Low' THEN N'Standard' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt new file mode 100644 index 0000000..927c6ff --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitch.verified.txt @@ -0,0 +1,12 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'High' THEN N'Premium' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END = N'Low' THEN N'Standard' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt new file mode 100644 index 0000000..409a445 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet10_0.verified.txt @@ -0,0 +1,19 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level3' THEN N'Expert' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level2' THEN N'Intermediate' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level1' THEN N'Beginner' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt new file mode 100644 index 0000000..409a445 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.DotNet9_0.verified.txt @@ -0,0 +1,19 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level3' THEN N'Expert' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level2' THEN N'Intermediate' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level1' THEN N'Beginner' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt new file mode 100644 index 0000000..409a445 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InSwitchExpression.verified.txt @@ -0,0 +1,19 @@ +SELECT CASE + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level3' THEN N'Expert' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level2' THEN N'Intermediate' + WHEN CASE + WHEN [e].[Value] > 100 THEN N'Level3' + WHEN [e].[Value] > 50 THEN N'Level2' + ELSE N'Level1' + END = N'Level1' THEN N'Beginner' + ELSE N'Unknown' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt new file mode 100644 index 0000000..ad971d0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet10_0.verified.txt @@ -0,0 +1,8 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt new file mode 100644 index 0000000..ad971d0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.DotNet9_0.verified.txt @@ -0,0 +1,8 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt new file mode 100644 index 0000000..ad971d0 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_InTernary.verified.txt @@ -0,0 +1,8 @@ +SELECT CASE + WHEN [e].[IsActive] = CAST(1 AS bit) THEN CASE + WHEN [e].[Value] > 100 THEN N'High' + ELSE N'Low' + END + ELSE N'Inactive' +END +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt new file mode 100644 index 0000000..69eb4b8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt new file mode 100644 index 0000000..69eb4b8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt new file mode 100644 index 0000000..69eb4b8 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Multiple.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt new file mode 100644 index 0000000..72fc7ea --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt new file mode 100644 index 0000000..72fc7ea --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt new file mode 100644 index 0000000..72fc7ea --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Nested.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 + 10 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt new file mode 100644 index 0000000..0bb6121 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt new file mode 100644 index 0000000..0bb6121 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt new file mode 100644 index 0000000..0bb6121 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_Simple.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] + 42 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt new file mode 100644 index 0000000..0294ea7 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet10_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 84 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt new file mode 100644 index 0000000..0294ea7 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.DotNet9_0.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 84 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt new file mode 100644 index 0000000..0294ea7 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.FunctionalTests/BlockBodiedMethods/BlockBodyProjectableCallTests.BlockBodyCallingProjectableMethod_WithLocalVariable.verified.txt @@ -0,0 +1,2 @@ +SELECT [e].[Value] * 2 + 84 +FROM [Entity] AS [e] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt new file mode 100644 index 0000000..a6b0b53 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_CompoundAssignment_ReportsError.verified.txt @@ -0,0 +1,3 @@ +[ + (11,13): error EFP0004: Compound assignment operator '+=' has side effects and cannot be used in projectable methods +] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt new file mode 100644 index 0000000..b5f9f5b --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_ImplicitReturn.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar > 10 ? 1 : default; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt new file mode 100644 index 0000000..c22d885 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IfWithoutElse_UsesDefault.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar > 10 ? 1 : 0; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt new file mode 100644 index 0000000..d47a3ba --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_IncrementOperator_ReportsError.verified.txt @@ -0,0 +1,3 @@ +[ + (12,13): error EFP0004: Increment/decrement operator '++' has side effects and cannot be used in projectable methods +] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt new file mode 100644 index 0000000..e940c26 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInIfCondition.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar * 2 > 10 ? 1 : 0; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt new file mode 100644 index 0000000..0a7e7da --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalInSwitchExpression.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar * 2 == 2 ? "Two" : @this.Bar * 2 == 4 ? "Four" : "Other"; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt new file mode 100644 index 0000000..587f792 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic.verified.txt @@ -0,0 +1,3 @@ +[ + (13,17): warning EFP0003: Method 'Foo' contains an unsupported statement: Local declarations in nested blocks are not supported +] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt new file mode 100644 index 0000000..26e6a19 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning.verified.txt @@ -0,0 +1,3 @@ +[ + (11,13): warning EFP0005: Method call 'WriteLine' may have side effects. Only calls to methods marked with [Projectable] are guaranteed to be safe in projectable methods +] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt new file mode 100644 index 0000000..e684d40 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_PropertyAssignment_ReportsError.verified.txt @@ -0,0 +1,3 @@ +[ + (11,13): error EFP0004: Assignment operation has side effects and cannot be used in projectable methods +] \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt new file mode 100644 index 0000000..eeb0754 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SimpleReturn.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => 42; + } + } +} diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt new file mode 100644 index 0000000..d1a7eb5 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_Simple.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar == 1 ? "One" : @this.Bar == 2 ? "Two" : "Other"; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt new file mode 100644 index 0000000..c90d6b7 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithMultipleCases.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar == 1 || @this.Bar == 2 ? "Low" : @this.Bar == 3 || @this.Bar == 4 || @this.Bar == 5 ? "Medium" : "High"; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt new file mode 100644 index 0000000..0a4d15d --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_SwitchStatement_WithoutDefault.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar == 1 ? "One" : @this.Bar == 2 ? "Two" : default; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt new file mode 100644 index 0000000..c22d885 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElse.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar > 10 ? 1 : 0; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt new file mode 100644 index 0000000..ef8f31a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithIfElseAndCondition.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.IsActive && @this.Bar > 0 ? @this.Bar * 2 : 0; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt new file mode 100644 index 0000000..d863659 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithLocalVariable.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar * 2 + 5; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt new file mode 100644 index 0000000..7c1426a --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithMultipleParameters.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Add_P0_int_P1_int + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this, int a, int b) => a + b; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt new file mode 100644 index 0000000..216b8f2 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithNestedIfElse.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar > 10 ? "High" : @this.Bar > 5 ? "Medium" : "Low"; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt new file mode 100644 index 0000000..19e29c9 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithPropertyAccess.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar + 10; + } + } +} \ No newline at end of file diff --git a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt new file mode 100644 index 0000000..24ae821 --- /dev/null +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.BlockBodiedMethod_WithTransitiveLocalVariables.verified.txt @@ -0,0 +1,17 @@ +// +#nullable disable +using System; +using EntityFrameworkCore.Projectables; +using Foo; + +namespace EntityFrameworkCore.Projectables.Generated +{ + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + static class Foo_C_Foo + { + static global::System.Linq.Expressions.Expression> Expression() + { + return (global::Foo.C @this) => @this.Bar * 2 + 5 + 10; + } + } +} \ 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 e5b07d2..ae09af6 100644 --- a/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs +++ b/tests/EntityFrameworkCore.Projectables.Generator.Tests/ProjectionExpressionGeneratorTests.cs @@ -493,7 +493,7 @@ public int Foo } [Fact] - public void BlockBodiedMethod_RaisesDiagnostics() + public void BlockBodiedMethod_NoLongerRaisesDiagnostics() { var compilation = CreateCompilation(@" using System; @@ -511,7 +511,9 @@ public int Foo() var result = RunGenerator(compilation); - Assert.Single(result.Diagnostics); + // Block-bodied methods are now supported, so no diagnostics should be raised + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); } [Fact] @@ -1977,6 +1979,636 @@ public static Dictionary ToDictionary(this Entity entity) return Verifier.Verify(result.GeneratedTrees[0].ToString()); } + [Fact] + public Task BlockBodiedMethod_SimpleReturn() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + [Projectable] + public int Foo() + { + return 42; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithPropertyAccess() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + return Bar + 10; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithIfElse() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + if (Bar > 10) + { + return 1; + } + else + { + return 0; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithNestedIfElse() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public string Foo() + { + if (Bar > 10) + { + return ""High""; + } + else if (Bar > 5) + { + return ""Medium""; + } + else + { + return ""Low""; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithLocalVariable() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + var temp = Bar * 2; + return temp + 5; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithTransitiveLocalVariables() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + var a = Bar * 2; + var b = a + 5; + return b + 10; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_LocalInIfCondition() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + var threshold = Bar * 2; + if (threshold > 10) + { + return 1; + } + else + { + return 0; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_LocalInSwitchExpression() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public string Foo() + { + var value = Bar * 2; + switch (value) + { + case 2: + return ""Two""; + case 4: + return ""Four""; + default: + return ""Other""; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_LocalsInNestedBlock_ProducesDiagnostic() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + if (Bar > 10) + { + var temp = Bar * 2; + return temp; + } + return 0; + } + } +} +", expectedToCompile: true); + + var result = RunGenerator(compilation); + + // Should have a diagnostic about locals in nested blocks + Assert.NotEmpty(result.Diagnostics); + Assert.Contains(result.Diagnostics, d => d.Id == "EFP0003"); + + return Verifier.Verify(result.Diagnostics.Select(d => d.ToString())); + } + + [Fact] + public Task BlockBodiedMethod_WithMultipleParameters() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + [Projectable] + public int Add(int a, int b) + { + return a + b; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_WithIfElseAndCondition() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + public bool IsActive { get; set; } + + [Projectable] + public int Foo() + { + if (IsActive && Bar > 0) + { + return Bar * 2; + } + else + { + return 0; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + + [Fact] + public Task BlockBodiedMethod_IfWithoutElse_UsesDefault() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + if (Bar > 10) + { + return 1; + } + return 0; + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_IfWithoutElse_ImplicitReturn() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int? Foo() + { + if (Bar > 10) + { + return 1; + } + } + } +} +", expectedToCompile: false); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_SwitchStatement_Simple() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public string Foo() + { + switch (Bar) + { + case 1: + return ""One""; + case 2: + return ""Two""; + default: + return ""Other""; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_SwitchStatement_WithMultipleCases() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public string Foo() + { + switch (Bar) + { + case 1: + case 2: + return ""Low""; + case 3: + case 4: + case 5: + return ""Medium""; + default: + return ""High""; + } + } + } +} +"); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_SwitchStatement_WithoutDefault() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public string? Foo() + { + switch (Bar) + { + case 1: + return ""One""; + case 2: + return ""Two""; + } + } + } +} +", expectedToCompile: false); + + var result = RunGenerator(compilation); + + Assert.Empty(result.Diagnostics); + Assert.Single(result.GeneratedTrees); + + return Verifier.Verify(result.GeneratedTrees[0].ToString()); + } + + [Fact] + public Task BlockBodiedMethod_PropertyAssignment_ReportsError() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + Bar = 10; + return Bar; + } + } +} +", expectedToCompile: true); + + var result = RunGenerator(compilation); + + // Should have a diagnostic about side effects + Assert.NotEmpty(result.Diagnostics); + Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004"); + + return Verifier.Verify(result.Diagnostics.Select(d => d.ToString())); + } + + [Fact] + public Task BlockBodiedMethod_CompoundAssignment_ReportsError() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + Bar += 10; + return Bar; + } + } +} +", expectedToCompile: true); + + var result = RunGenerator(compilation); + + // Should have a diagnostic about side effects + Assert.NotEmpty(result.Diagnostics); + Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004"); + + return Verifier.Verify(result.Diagnostics.Select(d => d.ToString())); + } + + [Fact] + public Task BlockBodiedMethod_IncrementOperator_ReportsError() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + var x = 5; + x++; + return x; + } + } +} +", expectedToCompile: true); + + var result = RunGenerator(compilation); + + // Should have a diagnostic about side effects + Assert.NotEmpty(result.Diagnostics); + Assert.Contains(result.Diagnostics, d => d.Id == "EFP0004"); + + return Verifier.Verify(result.Diagnostics.Select(d => d.ToString())); + } + + [Fact] + public Task BlockBodiedMethod_NonProjectableMethodCall_ReportsWarning() + { + var compilation = CreateCompilation(@" +using System; +using EntityFrameworkCore.Projectables; +namespace Foo { + class C { + public int Bar { get; set; } + + [Projectable] + public int Foo() + { + Console.WriteLine(""test""); + return Bar; + } + } +} +", expectedToCompile: true); + + var result = RunGenerator(compilation); + + // Should have a diagnostic about potential side effects + Assert.NotEmpty(result.Diagnostics); + Assert.Contains(result.Diagnostics, d => d.Id == "EFP0005"); + + return Verifier.Verify(result.Diagnostics.Select(d => d.ToString())); + } + [Fact] public Task MethodOverloads_WithDifferentParameterTypes() {