This is an automated email from the ASF dual-hosted git repository.

emilles pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new d5b22aaf4f GROOVY-11565: empty block handling
d5b22aaf4f is described below

commit d5b22aaf4fc26a52893fb2a0cc61941fb79288c8
Author: Eric Milles <[email protected]>
AuthorDate: Sat Feb 8 08:58:50 2025 -0600

    GROOVY-11565: empty block handling
    
    3_0_X backport
---
 src/main/antlr2/org/codehaus/groovy/antlr/groovy.g | 32 ++++-----
 .../codehaus/groovy/antlr/AntlrParserPlugin.java   | 13 ++--
 .../apache/groovy/parser/antlr4/AstBuilder.java    | 83 ++++++----------------
 .../groovy/parser/antlr4/GroovyParserTest.groovy   | 11 +--
 .../src/test/resources/core/IfElse_02.groovy       | 47 ++++++++++++
 5 files changed, 89 insertions(+), 97 deletions(-)

diff --git a/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g 
b/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
index 1f70720c57..bb26e10937 100644
--- a/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
+++ b/src/main/antlr2/org/codehaus/groovy/antlr/groovy.g
@@ -1718,35 +1718,31 @@ statement[int prevToken]
     // side-effects.
     // The prevToken is used to check for dumb expressions like +1.
     |    es:expressionStatement[prevToken]
-        //{#statement = #(create(EXPR,"EXPR",first,LT(1)), es);}
 
-    // If-else statement
-    |   "if"! LPAREN! ale:assignmentLessExpression! RPAREN! nlsWarn! 
ifCbs:compatibleBodyStatement!
+    // If-then-else statement
+    |   "if"! LPAREN! condExpr:assignmentLessExpression! RPAREN! nlsWarn! 
(skipStmt:SEMI! | thenStmt:compatibleBodyStatement!)
         (
-            // CONFLICT: the old "dangling-else" problem...
-            //           ANTLR generates proper code matching
-            //                       as soon as possible.  Hush warning.
-            options {
-                    warnWhenFollowAmbig = false;
-            }
-        :   // lookahead to check if we're entering an 'else' clause
             ( (sep!)? "else"! )=>
-            (sep!)?  // allow SEMI here for compatibility with Java
-            "else"! nlsWarn! elseCbs:compatibleBodyStatement!
+            (sep!)? // "if (cond) 1; else 2" -- dangling SEMI!
+            "else"! nlsWarn! elseStmt:compatibleBodyStatement!
         )?
-        {#statement = #(create(LITERAL_if,"if",first,LT(1)), ale, ifCbs, 
elseCbs);}
+        {
+            if (#skipStmt != null)
+                #statement = 
#(create(LITERAL_if,"if",first,LT(1)),condExpr,skipStmt,elseStmt);
+            else
+                #statement = 
#(create(LITERAL_if,"if",first,LT(1)),condExpr,thenStmt,elseStmt);
+        }
 
     // For statement
     |   forStatement
 
     // While statement
-    |   "while"! LPAREN! sce=while_sce:strictContextExpression[false]! RPAREN! 
nlsWarn!
-        (s:SEMI! | while_cbs:compatibleBodyStatement!)
+    |   "while"! LPAREN! sce=whileExpr:strictContextExpression[false]! RPAREN! 
nlsWarn! (emptyStmt:SEMI! | whileStmt:compatibleBodyStatement!)
         {
-            if (#s != null)
-                #statement = 
#(create(LITERAL_while,"Literal_while",first,LT(1)), while_sce, s);
+            if (#emptyStmt != null)
+                #statement = 
#(create(LITERAL_while,"Literal_while",first,LT(1)),whileExpr,emptyStmt);
             else
-                #statement = 
#(create(LITERAL_while,"Literal_while",first,LT(1)), while_sce, while_cbs);
+                #statement = 
#(create(LITERAL_while,"Literal_while",first,LT(1)),whileExpr,whileStmt);
         }
 
     // Import statement.  Can be used in any scope.  Has "import x as y" also.
diff --git a/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java 
b/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
index 304cf9c4f9..d774b54556 100644
--- a/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
+++ b/src/main/java/org/codehaus/groovy/antlr/AntlrParserPlugin.java
@@ -1486,19 +1486,16 @@ public class AntlrParserPlugin extends ASTHelper 
implements ParserPlugin, Groovy
     }
 
     protected Statement ifStatement(AST ifNode) {
-        AST node = ifNode.getFirstChild();
-        assertNodeType(EXPR, node);
+        AST node = ifNode.getFirstChild(); assertNodeType(EXPR, node);
         BooleanExpression booleanExpression = booleanExpression(node);
 
         node = node.getNextSibling();
-        Statement ifBlock = statement(node);
+        Statement thenBlock = isType(SEMI, node) ? EmptyStatement.INSTANCE : 
statement(node); // GROOVY-11565
 
-        Statement elseBlock = EmptyStatement.INSTANCE;
         node = node.getNextSibling();
-        if (node != null) {
-            elseBlock = statement(node);
-        }
-        IfStatement ifStatement = new IfStatement(booleanExpression, ifBlock, 
elseBlock);
+        Statement elseBlock = (node == null || isType(SEMI, node)) ? 
EmptyStatement.INSTANCE : statement(node);
+
+        IfStatement ifStatement = new IfStatement(booleanExpression, 
thenBlock, elseBlock);
         configureAST(ifStatement, ifNode);
         return ifStatement;
     }
diff --git 
a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
 
b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
index 9e2d25bc3f..90c2b8fc98 100644
--- 
a/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
+++ 
b/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
@@ -35,7 +35,6 @@ import org.antlr.v4.runtime.misc.Interval;
 import org.antlr.v4.runtime.misc.ParseCancellationException;
 import org.antlr.v4.runtime.tree.ParseTree;
 import org.antlr.v4.runtime.tree.TerminalNode;
-import org.apache.groovy.parser.antlr4.GroovyParser.*;
 import org.apache.groovy.parser.antlr4.internal.DescriptiveErrorStrategy;
 import org.apache.groovy.parser.antlr4.internal.atnmanager.AtnManager;
 import org.apache.groovy.parser.antlr4.util.StringUtils;
@@ -146,25 +145,7 @@ import java.util.stream.Collectors;
 import java.util.stream.Stream;
 
 import static groovy.lang.Tuple.tuple;
-import static org.apache.groovy.parser.antlr4.GroovyParser.ADD;
-import static org.apache.groovy.parser.antlr4.GroovyParser.AS;
-import static org.apache.groovy.parser.antlr4.GroovyParser.CASE;
-import static org.apache.groovy.parser.antlr4.GroovyParser.DEC;
-import static org.apache.groovy.parser.antlr4.GroovyParser.DEF;
-import static org.apache.groovy.parser.antlr4.GroovyParser.DEFAULT;
-import static org.apache.groovy.parser.antlr4.GroovyParser.GE;
-import static org.apache.groovy.parser.antlr4.GroovyParser.GT;
-import static org.apache.groovy.parser.antlr4.GroovyParser.IN;
-import static org.apache.groovy.parser.antlr4.GroovyParser.INC;
-import static org.apache.groovy.parser.antlr4.GroovyParser.INSTANCEOF;
-import static org.apache.groovy.parser.antlr4.GroovyParser.LE;
-import static org.apache.groovy.parser.antlr4.GroovyParser.LT;
-import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_IN;
-import static org.apache.groovy.parser.antlr4.GroovyParser.NOT_INSTANCEOF;
-import static org.apache.groovy.parser.antlr4.GroovyParser.PRIVATE;
-import static org.apache.groovy.parser.antlr4.GroovyParser.STATIC;
-import static org.apache.groovy.parser.antlr4.GroovyParser.SUB;
-import static org.apache.groovy.parser.antlr4.GroovyParser.VAR;
+import static org.apache.groovy.parser.antlr4.GroovyParser.*;
 import static 
org.apache.groovy.parser.antlr4.util.PositionConfigureUtils.configureAST;
 import static org.codehaus.groovy.classgen.asm.util.TypeUtil.isPrimitiveType;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
@@ -445,29 +426,22 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
     @Override
     public IfStatement visitIfElseStatement(final IfElseStatementContext ctx) {
         Expression conditionExpression = 
this.visitExpressionInPar(ctx.expressionInPar());
-        BooleanExpression booleanExpression =
-                configureAST(
-                        new BooleanExpression(conditionExpression), 
conditionExpression);
+        BooleanExpression booleanExpression = configureAST(new 
BooleanExpression(conditionExpression), conditionExpression);
 
-        Statement ifBlock =
-                this.unpackStatement(
-                        (Statement) this.visit(ctx.tb));
-        Statement elseBlock =
-                this.unpackStatement(
-                        asBoolean(ctx.ELSE())
-                                ? (Statement) this.visit(ctx.fb)
-                                : EmptyStatement.INSTANCE);
+        Statement thenStatement =                      
this.unpackStatement((Statement) this.visit(ctx.tb))                          ;
+        Statement elseStatement = ctx.ELSE() != null ? 
this.unpackStatement((Statement) this.visit(ctx.fb)) : EmptyStatement.INSTANCE;
 
-        return configureAST(new IfStatement(booleanExpression, ifBlock, 
elseBlock), ctx);
+        return configureAST(new IfStatement(booleanExpression, thenStatement, 
elseStatement), ctx);
     }
 
     @Override
     public Statement visitLoopStmtAlt(final LoopStmtAltContext ctx) {
         visitingLoopStatementCount += 1;
-        Statement result = configureAST((Statement) 
this.visit(ctx.loopStatement()), ctx);
-        visitingLoopStatementCount -= 1;
-
-        return result;
+        try {
+            return configureAST((Statement) this.visit(ctx.loopStatement()), 
ctx);
+        } finally {
+            visitingLoopStatementCount -= 1;
+        }
     }
 
     @Override
@@ -476,9 +450,7 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
 
         Statement loopBlock = this.unpackStatement((Statement) 
this.visit(ctx.statement()));
 
-        return configureAST(
-                new ForStatement(controlTuple.getV1(), controlTuple.getV2(), 
asBoolean(loopBlock) ? loopBlock : EmptyStatement.INSTANCE),
-                ctx);
+        return configureAST(new ForStatement(controlTuple.getV1(), 
controlTuple.getV2(), loopBlock), ctx);
     }
 
     @Override
@@ -502,12 +474,12 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
 
         if (asBoolean(ctx.localVariableDeclaration())) {
             DeclarationListStatement declarationListStatement = 
this.visitLocalVariableDeclaration(ctx.localVariableDeclaration());
-            List<DeclarationExpression> declarationExpressions = 
declarationListStatement.getDeclarationExpressions();
+            List<? extends Expression> declarationExpressions = 
declarationListStatement.getDeclarationExpressions();
 
             if (declarationExpressions.size() == 1) {
-                return configureAST((Expression) 
declarationExpressions.get(0), ctx);
+                return configureAST(declarationExpressions.get(0), ctx);
             } else {
-                return configureAST(new ClosureListExpression((List) 
declarationExpressions), ctx);
+                return configureAST(new 
ClosureListExpression((List<Expression>) declarationExpressions), ctx);
             }
         }
 
@@ -563,28 +535,19 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
     public WhileStatement visitWhileStmtAlt(final WhileStmtAltContext ctx) {
         Tuple2<BooleanExpression, Statement> conditionAndBlock = 
createLoopConditionExpressionAndBlock(ctx.expressionInPar(), ctx.statement());
 
-        return configureAST(
-                new WhileStatement(conditionAndBlock.getV1(), 
asBoolean(conditionAndBlock.getV2()) ? conditionAndBlock.getV2() : 
EmptyStatement.INSTANCE),
-                ctx);
+        return configureAST(new WhileStatement(conditionAndBlock.getV1(), 
conditionAndBlock.getV2()), ctx);
     }
 
     @Override
     public DoWhileStatement visitDoWhileStmtAlt(final DoWhileStmtAltContext 
ctx) {
         Tuple2<BooleanExpression, Statement> conditionAndBlock = 
createLoopConditionExpressionAndBlock(ctx.expressionInPar(), ctx.statement());
 
-        return configureAST(
-                new DoWhileStatement(conditionAndBlock.getV1(), 
asBoolean(conditionAndBlock.getV2()) ? conditionAndBlock.getV2() : 
EmptyStatement.INSTANCE),
-                ctx);
+        return configureAST(new DoWhileStatement(conditionAndBlock.getV1(), 
conditionAndBlock.getV2()), ctx);
     }
 
     private Tuple2<BooleanExpression, Statement> 
createLoopConditionExpressionAndBlock(final ExpressionInParContext eipc, final 
StatementContext sc) {
         Expression conditionExpression = this.visitExpressionInPar(eipc);
-
-        BooleanExpression booleanExpression =
-                configureAST(
-                        new BooleanExpression(conditionExpression),
-                        conditionExpression
-                );
+        BooleanExpression booleanExpression = configureAST(new 
BooleanExpression(conditionExpression), conditionExpression);
 
         Statement loopBlock = this.unpackStatement((Statement) this.visit(sc));
 
@@ -4080,16 +4043,12 @@ public class AstBuilder extends 
GroovyParserBaseVisitor<Object> {
 
     private Statement unpackStatement(final Statement statement) {
         if (statement instanceof DeclarationListStatement) {
-            List<ExpressionStatement> expressionStatementList = 
((DeclarationListStatement) statement).getDeclarationStatements();
-
-            if (1 == expressionStatementList.size()) {
-                return expressionStatementList.get(0);
-            }
-
-            return configureAST(this.createBlockStatement(statement), 
statement); // if DeclarationListStatement contains more than 1 declarations, 
maybe it's better to create a block to hold them
+            // if DeclarationListStatement contains more than 1 declarations, 
maybe it's better to create a block to hold them
+            List<ExpressionStatement> expressionStatements = 
((DeclarationListStatement) statement).getDeclarationStatements();
+            return expressionStatements.size() == 1 ? 
expressionStatements.get(0) : 
configureAST(this.createBlockStatement(statement), statement);
         }
 
-        return statement;
+        return Optional.ofNullable(statement).orElse(EmptyStatement.INSTANCE);
     }
 
     BlockStatement createBlockStatement(final Statement... statements) {
diff --git 
a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
 
b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
index c48afea9ac..8f264d078e 100644
--- 
a/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
+++ 
b/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
@@ -237,7 +237,8 @@ final class GroovyParserTest extends GroovyTestCase {
     }
 
     void "test groovy core - IfElse"() {
-        doTest('core/IfElse_01.groovy', [AssertStatement])
+        doTest('core/IfElse_01.groovy')
+        doTest('core/IfElse_02.groovy')
     }
 
     void "test groovy core - For"() {
@@ -264,7 +265,6 @@ final class GroovyParserTest extends GroovyTestCase {
         doRunAndTestAntlr4('core/DoWhile_04x.groovy')
     }
 
-
     void "test groovy core - TryCatch"() {
         doTest('core/TryCatch_01.groovy')
     }
@@ -291,7 +291,6 @@ final class GroovyParserTest extends GroovyTestCase {
         doRunAndTestAntlr4('core/DefaultMethod_02x.groovy')
     }
 
-
     void "test groovy core - Switch"() {
         doTest('core/Switch_01.groovy')
     }
@@ -373,12 +372,6 @@ final class GroovyParserTest extends GroovyTestCase {
         doRunAndTestAntlr4('core/Command_07x.groovy')
     }
 
-    /*
-    void "test groovy core - Unicode"() {
-        doTest('core/Unicode_01.groovy')
-    }
-    */
-
     void "test groovy core - BreakingChanges"() {
         doRunAndTestAntlr4('core/BreakingChange_01x.groovy')
         doRunAndTestAntlr4('core/BreakingChange_02x.groovy')
diff --git a/subprojects/parser-antlr4/src/test/resources/core/IfElse_02.groovy 
b/subprojects/parser-antlr4/src/test/resources/core/IfElse_02.groovy
new file mode 100644
index 0000000000..b21ac75a60
--- /dev/null
+++ b/subprojects/parser-antlr4/src/test/resources/core/IfElse_02.groovy
@@ -0,0 +1,47 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+
+// GROOVY-11565: empty block handling
+
+if (true);
+if (true) ;
+if (true)
+  ;
+
+if (true){}
+if (true) {}
+if (true)
+  {
+  }
+
+if (true);else 0
+if (true) ; else 0
+if (true)
+  ;
+else
+  0
+
+if (true){}else{}
+if (true) {} else {}
+if (true)
+  {
+  }
+else
+  {
+  }

Reply via email to