From bd762a9a9950ba85a5457d1aa67794912b739c0b Mon Sep 17 00:00:00 2001 From: Koen Date: Sun, 2 Aug 2026 00:31:35 +0000 Subject: [PATCH] support locals before return in block-bodied switch cases --- .../Emitter/ExpressionTreeEmitter.cs | 30 +++++++++++++++-- .../ExpressiveGenerator/BlockBodyTests.cs | 32 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs b/src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs index aea48924..a112c4df 100644 --- a/src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs +++ b/src/ExpressiveSharp.Generator/Emitter/ExpressionTreeEmitter.cs @@ -2107,8 +2107,12 @@ private string EmitSwitchStatement(ISwitchOperation switchStmt) return currentVar; } - /// Returns the single return operation in a case body, or null if the shape isn't supported. - private static IReturnOperation? FindCaseReturn(ISwitchCaseOperation switchCase) + /// + /// Returns the case's return operation, inlining any leading local declarations (their + /// initializer expressions are mapped so the returned value embeds them and evaluation stays + /// inside the arm); null if the shape isn't supported. + /// + private IReturnOperation? FindCaseReturn(ISwitchCaseOperation switchCase) { foreach (var op in switchCase.Body) { @@ -2123,6 +2127,10 @@ private string EmitSwitchStatement(ISwitchOperation switchStmt) { return innerRet; } + if (inner is IVariableDeclarationGroupOperation declGroup && TryInlineCaseLocals(declGroup)) + { + continue; + } return null; } return null; @@ -2133,6 +2141,24 @@ private string EmitSwitchStatement(ISwitchOperation switchStmt) return null; } + private bool TryInlineCaseLocals(IVariableDeclarationGroupOperation declGroup) + { + foreach (var declaration in declGroup.Declarations) + { + foreach (var declarator in declaration.Declarators) + { + if (declarator.Initializer is null) + { + return false; + } + + _localToVar[declarator.Symbol] = EmitOperation(declarator.Initializer.Value); + } + } + + return true; + } + private string EmitSwitchExpression(ISwitchExpressionOperation switchExpr) { var governingVar = EmitOperation(switchExpr.Value); diff --git a/tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/BlockBodyTests.cs b/tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/BlockBodyTests.cs index e12b0fe8..4fa85ca3 100644 --- a/tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/BlockBodyTests.cs +++ b/tests/ExpressiveSharp.Generator.Tests/ExpressiveGenerator/BlockBodyTests.cs @@ -389,4 +389,36 @@ public int Foo() return Verifier.Verify(result.GeneratedTrees[0].ToString()); } + + [TestMethod] + public void SwitchCase_LocalBeforeReturn_IsSupported() + { + var compilation = CreateCompilation( + """ + namespace Foo { + class C { + [Expressive(AllowBlockBody = true)] + public static string Label(int x) + { + switch (x) + { + case 1: + { + var y = "one"; + return y; + } + default: + return "other"; + } + } + } + } + """); + + var result = RunExpressiveGenerator(compilation); + + Assert.IsFalse(result.Diagnostics.Any(d => d.Id == "EXP0008"), + "A case block with a local before the return should be emitted, not degraded to a " + + "default value via EXP0008. Diagnostics: " + string.Join("; ", result.Diagnostics)); + } }