You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by su...@apache.org on 2017/06/24 02:50:47 UTC
[1/2] groovy git commit: Support JSR308
Repository: groovy
Updated Branches:
refs/heads/master 05f1e28f7 -> bc2f4eb50
Support JSR308
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/c659f4ad
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/c659f4ad
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/c659f4ad
Branch: refs/heads/master
Commit: c659f4ad8c48181a8f19b866bbd93b7baf97cfa6
Parents: 141fccf
Author: sunlan <su...@apache.org>
Authored: Tue Jun 20 22:08:55 2017 +0800
Committer: sunlan <su...@apache.org>
Committed: Tue Jun 20 22:08:55 2017 +0800
----------------------------------------------------------------------
.../apache/groovy/parser/antlr4/GroovyParser.g4 | 57 +++++--
.../apache/groovy/parser/antlr4/AstBuilder.java | 147 ++++++++++++++-----
.../parser/antlr4/GroovyParserTest.groovy | 4 +-
.../src/test/resources/bugs/GROOVY-8228.groovy | 92 ++++++++++++
4 files changed, 249 insertions(+), 51 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/groovy/blob/c659f4ad/subprojects/parser-antlr4/src/main/antlr4/org/apache/groovy/parser/antlr4/GroovyParser.g4
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/main/antlr4/org/apache/groovy/parser/antlr4/GroovyParser.g4 b/subprojects/parser-antlr4/src/main/antlr4/org/apache/groovy/parser/antlr4/GroovyParser.g4
index 6f644ef..b7efd4f 100644
--- a/subprojects/parser-antlr4/src/main/antlr4/org/apache/groovy/parser/antlr4/GroovyParser.g4
+++ b/subprojects/parser-antlr4/src/main/antlr4/org/apache/groovy/parser/antlr4/GroovyParser.g4
@@ -334,19 +334,38 @@ variableInitializers
: variableInitializer nls (COMMA nls variableInitializer nls)* nls COMMA?
;
+dims
+ : (annotationsOpt LBRACK RBRACK)+
+ ;
+
+dimsOpt
+ : dims?
+ ;
+
standardType
options { baseContext = type; }
- : primitiveType (LBRACK RBRACK)*
- | standardClassOrInterfaceType (LBRACK RBRACK)*
+ : annotationsOpt
+ (
+ primitiveType
+ |
+ standardClassOrInterfaceType
+ )
+ dimsOpt
;
type
- : ( primitiveType
+ : annotationsOpt
+ (
+ (
+ primitiveType
+ |
+ // !!! ERROR ALTERNATIVE !!!
+ VOID { require(false, "void is not allowed here", -4); }
+ )
|
- // !!! ERROR ALTERNATIVE !!!
- VOID { require(false, "void is not allowed here", -4); }
- ) (LBRACK RBRACK)*
- | generalClassOrInterfaceType (LBRACK RBRACK)*
+ generalClassOrInterfaceType
+ )
+ dimsOpt
;
classOrInterfaceType
@@ -375,11 +394,15 @@ typeArguments
typeArgument
: type
- | QUESTION ((EXTENDS | SUPER) nls type)?
+ | annotationsOpt QUESTION ((EXTENDS | SUPER) nls type)?
+ ;
+
+annotatedQualifiedClassName
+ : annotationsOpt qualifiedClassName
;
qualifiedClassNameList
- : qualifiedClassName (COMMA nls qualifiedClassName)*
+ : annotatedQualifiedClassName (COMMA nls annotatedQualifiedClassName)*
;
formalParameters
@@ -387,10 +410,14 @@ formalParameters
;
formalParameterList
- : formalParameter (COMMA nls formalParameter)* (COMMA nls lastFormalParameter)?
+ : (formalParameter | thisFormalParameter) (COMMA nls formalParameter)* (COMMA nls lastFormalParameter)?
| lastFormalParameter
;
+thisFormalParameter
+ : type THIS
+ ;
+
formalParameter
: variableModifiersOpt type? variableDeclaratorId (nls ASSIGN nls expression)?
;
@@ -1079,8 +1106,8 @@ mapEntryLabel
creator
: createdName
( nls arguments anonymousInnerClassDeclaration[0]?
- | (LBRACK expression RBRACK)+ (b+=LBRACK RBRACK)*
- | (b+=LBRACK RBRACK)+ nls arrayInitializer
+ | (annotationsOpt LBRACK expression RBRACK)+ dimsOpt
+ | dims nls arrayInitializer
)
;
@@ -1096,8 +1123,10 @@ anonymousInnerClassDeclaration[int t]
;
createdName
- : primitiveType
- | qualifiedClassName typeArgumentsOrDiamond?
+ : annotationsOpt
+ ( primitiveType
+ | qualifiedClassName typeArgumentsOrDiamond?
+ )
;
nonWildcardTypeArguments
http://git-wip-us.apache.org/repos/asf/groovy/blob/c659f4ad/subprojects/parser-antlr4/src/main/java/org/apache/groovy/parser/antlr4/AstBuilder.java
----------------------------------------------------------------------
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 7ac33a0..aa2d0f8 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
@@ -262,7 +262,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
PackageNode packageNode = moduleNode.getPackage();
- this.visitAnnotationsOpt(ctx.annotationsOpt()).forEach(packageNode::addAnnotation);
+ packageNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
return this.configureAST(packageNode, ctx);
}
@@ -1028,7 +1028,7 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
this.visitIdentifier(ctx.identifier()),
createEnumConstantInitExpression(ctx.arguments(), anonymousInnerClassNode));
- this.visitAnnotationsOpt(ctx.annotationsOpt()).forEach(enumConstant::addAnnotation);
+ enumConstant.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
groovydocManager.handle(enumConstant, ctx);
@@ -2739,28 +2739,35 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
ctx);
}
- if (asBoolean(ctx.LBRACK())) { // create array
+ if (asBoolean(ctx.LBRACK()) || asBoolean(ctx.dims())) { // create array
+ ArrayExpression arrayExpression;
+ List<List<AnnotationNode>> allDimList;
+
if (asBoolean(ctx.arrayInitializer())) {
- ClassNode arrayType = classNode;
- for (int i = 0, n = ctx.b.size() - 1; i < n; i++) {
- arrayType = arrayType.makeArray();
+ ClassNode elementType = classNode;
+ allDimList = this.visitDims(ctx.dims());
+
+ for (int i = 0, n = allDimList.size() - 1; i < n; i++) {
+ elementType = elementType.makeArray();
}
- return this.configureAST(
+ arrayExpression =
new ArrayExpression(
- arrayType,
- this.visitArrayInitializer(ctx.arrayInitializer())),
- ctx);
+ elementType,
+ this.visitArrayInitializer(ctx.arrayInitializer()));
+
} else {
Expression[] empties;
- if (asBoolean(ctx.b)) {
- empties = new Expression[ctx.b.size()];
+ List<List<AnnotationNode>> emptyDimList = this.visitDimsOpt(ctx.dimsOpt());
+
+ if (asBoolean(emptyDimList)) {
+ empties = new Expression[emptyDimList.size()];
Arrays.setAll(empties, i -> ConstantExpression.EMPTY_EXPRESSION);
} else {
empties = new Expression[0];
}
- return this.configureAST(
+ arrayExpression =
new ArrayExpression(
classNode,
null,
@@ -2768,14 +2775,33 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
ctx.expression().stream()
.map(e -> (Expression) this.visit(e)),
Arrays.stream(empties)
- ).collect(Collectors.toList())),
- ctx);
+ ).collect(Collectors.toList()));
+
+
+ List<List<AnnotationNode>> exprDimList = ctx.annotationsOpt().stream().map(this::visitAnnotationsOpt).collect(Collectors.toList());
+ allDimList = new ArrayList<>(exprDimList);
+ Collections.reverse(emptyDimList);
+ allDimList.addAll(emptyDimList);
+ Collections.reverse(allDimList);
}
+
+ arrayExpression.setType(createArrayType(classNode, allDimList));
+
+ return this.configureAST(arrayExpression, ctx);
}
throw createParsingFailedException("Unsupported creator: " + ctx.getText(), ctx);
}
+ private ClassNode createArrayType(ClassNode classNode, List<List<AnnotationNode>> dimList) {
+ ClassNode arrayType = classNode;
+ for (int i = 0, n = dimList.size(); i < n; i++) {
+ arrayType = arrayType.makeArray();
+ arrayType.addAnnotations(dimList.get(i));
+ }
+ return arrayType;
+ }
+
private String genAnonymousClassName(String outerClassName) {
return outerClassName + "$" + this.anonymousInnerClassCounter++;
@@ -2819,24 +2845,30 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
@Override
public ClassNode visitCreatedName(CreatedNameContext ctx) {
+ ClassNode classNode = null;
+
if (asBoolean(ctx.qualifiedClassName())) {
- ClassNode classNode = this.visitQualifiedClassName(ctx.qualifiedClassName());
+ classNode = this.visitQualifiedClassName(ctx.qualifiedClassName());
if (asBoolean(ctx.typeArgumentsOrDiamond())) {
classNode.setGenericsTypes(
this.visitTypeArgumentsOrDiamond(ctx.typeArgumentsOrDiamond()));
}
- return this.configureAST(classNode, ctx);
- }
-
- if (asBoolean(ctx.primitiveType())) {
- return this.configureAST(
+ classNode = this.configureAST(classNode, ctx);
+ } else if (asBoolean(ctx.primitiveType())) {
+ classNode = this.configureAST(
this.visitPrimitiveType(ctx.primitiveType()),
ctx);
}
- throw createParsingFailedException("Unsupported created name: " + ctx.getText(), ctx);
+ if (!asBoolean(classNode)) {
+ throw createParsingFailedException("Unsupported created name: " + ctx.getText(), ctx);
+ }
+
+ classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+
+ return classNode;
}
@@ -3232,6 +3264,10 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
List<Parameter> parameterList = new LinkedList<>();
+ if (asBoolean(ctx.thisFormalParameter())) {
+ parameterList.add(this.visitThisFormalParameter(ctx.thisFormalParameter()));
+ }
+
if (asBoolean(ctx.formalParameter())) {
parameterList.addAll(
ctx.formalParameter().stream()
@@ -3252,6 +3288,11 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
}
@Override
+ public Parameter visitThisFormalParameter(ThisFormalParameterContext ctx) {
+ return this.configureAST(new Parameter(this.visitType(ctx.type()), THIS_STR), ctx);
+ }
+
+ @Override
public Parameter visitLastFormalParameter(LastFormalParameterContext ctx) {
return this.processFormalParameter(ctx, ctx.variableModifiersOpt(), ctx.type(), ctx.ELLIPSIS(), ctx.variableDeclaratorId(), ctx.expression());
}
@@ -3345,6 +3386,26 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
.collect(Collectors.toList());
}
+ @Override
+ public List<List<AnnotationNode>> visitDims(DimsContext ctx) {
+ List<List<AnnotationNode>> dimList =
+ ctx.annotationsOpt().stream()
+ .map(this::visitAnnotationsOpt)
+ .collect(Collectors.toList());
+
+ Collections.reverse(dimList);
+
+ return dimList;
+ }
+
+ @Override
+ public List<List<AnnotationNode>> visitDimsOpt(DimsOptContext ctx) {
+ if (!asBoolean(ctx.dims())) {
+ return Collections.emptyList();
+ }
+
+ return this.visitDims(ctx.dims());
+ }
// type { --------------------------------------------------------------------
@Override
@@ -3358,24 +3419,23 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
if (asBoolean(ctx.classOrInterfaceType())) {
ctx.classOrInterfaceType().putNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR, ctx.getNodeMetaData(IS_INSIDE_INSTANCEOF_EXPR));
classNode = this.visitClassOrInterfaceType(ctx.classOrInterfaceType());
+ } else if (asBoolean(ctx.primitiveType())) {
+ classNode = this.visitPrimitiveType(ctx.primitiveType());
}
- if (asBoolean(ctx.primitiveType())) {
- classNode = this.visitPrimitiveType(ctx.primitiveType());
+ if (!asBoolean(classNode)) {
+ throw createParsingFailedException("Unsupported type: " + ctx.getText(), ctx);
}
- if (asBoolean(ctx.LBRACK())) {
+ classNode.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+
+ List<List<AnnotationNode>> dimList = this.visitDimsOpt(ctx.dimsOpt());
+ if (asBoolean(dimList)) {
// clear array's generics type info. Groovy's bug? array's generics type will be ignored. e.g. List<String>[]... p
classNode.setGenericsTypes(null);
classNode.setUsingGenerics(false);
- for (int i = 0, n = ctx.LBRACK().size(); i < n; i++) {
- classNode = this.configureAST(classNode.makeArray(), classNode);
- }
- }
-
- if (!asBoolean(classNode)) {
- throw createParsingFailedException("Unsupported type: " + ctx.getText(), ctx);
+ classNode = this.createArrayType(classNode, dimList);
}
return this.configureAST(classNode, ctx);
@@ -3424,6 +3484,8 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
if (asBoolean(ctx.QUESTION())) {
ClassNode baseType = this.configureAST(ClassHelper.makeWithoutCaching(QUESTION_STR), ctx.QUESTION());
+ baseType.addAnnotations(this.visitAnnotationsOpt(ctx.annotationsOpt()));
+
if (!asBoolean(ctx.type())) {
GenericsType genericsType = new GenericsType(baseType);
genericsType.setWildcard(true);
@@ -3637,13 +3699,22 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
}
@Override
+ public ClassNode visitAnnotatedQualifiedClassName(AnnotatedQualifiedClassNameContext ctx) {
+ ClassNode classNode = this.visitQualifiedClassName(ctx.qualifiedClassName());
+
+ this.visitAnnotationsOpt(ctx.annotationsOpt()).forEach(classNode::addAnnotation);
+
+ return classNode;
+ }
+
+ @Override
public ClassNode[] visitQualifiedClassNameList(QualifiedClassNameListContext ctx) {
if (!asBoolean(ctx)) {
return new ClassNode[0];
}
- return ctx.qualifiedClassName().stream()
- .map(this::visitQualifiedClassName)
+ return ctx.annotatedQualifiedClassName().stream()
+ .map(this::visitAnnotatedQualifiedClassName)
.toArray(ClassNode[]::new);
}
@@ -3754,8 +3825,12 @@ public class AstBuilder extends GroovyParserBaseVisitor<Object> implements Groov
new ModifierManager(this, this.visitVariableModifiersOpt(variableModifiersOptContext))
.processParameter(
this.configureAST(
- new Parameter(classNode, this.visitVariableDeclaratorId(variableDeclaratorIdContext).getName()),
- ctx)
+ new Parameter(
+ classNode,
+ this.visitVariableDeclaratorId(variableDeclaratorIdContext).getName()
+ ),
+ ctx
+ )
);
if (asBoolean(expressionContext)) {
http://git-wip-us.apache.org/repos/asf/groovy/blob/c659f4ad/subprojects/parser-antlr4/src/test/groovy/org/apache/groovy/parser/antlr4/GroovyParserTest.groovy
----------------------------------------------------------------------
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 480d2ab..8e3ec65 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
@@ -357,12 +357,14 @@ class GroovyParserTest extends GroovyTestCase {
void "test groovy core - BUG"() {
doRunAndTest('bugs/BUG-GROOVY-4757.groovy');
- doRunAndTest('bugs/GROOVY-3898.groovy');
doRunAndTest('bugs/BUG-GROOVY-5652.groovy');
doRunAndTest('bugs/BUG-GROOVY-4762.groovy');
doRunAndTest('bugs/BUG-GROOVY-4438.groovy');
doRunAndTest('bugs/BUG-GROOVY-6038.groovy');
doRunAndTest('bugs/BUG-GROOVY-2324.groovy');
doTest('bugs/BUG-GROOVY-8161.groovy');
+
+ doRunAndTest('bugs/GROOVY-3898.groovy');
+ doRunAndTest('bugs/GROOVY-8228.groovy');
}
}
http://git-wip-us.apache.org/repos/asf/groovy/blob/c659f4ad/subprojects/parser-antlr4/src/test/resources/bugs/GROOVY-8228.groovy
----------------------------------------------------------------------
diff --git a/subprojects/parser-antlr4/src/test/resources/bugs/GROOVY-8228.groovy b/subprojects/parser-antlr4/src/test/resources/bugs/GROOVY-8228.groovy
new file mode 100644
index 0000000..a82c58d
--- /dev/null
+++ b/subprojects/parser-antlr4/src/test/resources/bugs/GROOVY-8228.groovy
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+import java.lang.annotation.Retention
+import java.lang.annotation.Target
+import java.lang.reflect.Field
+import java.lang.reflect.Method
+import java.lang.reflect.Parameter
+import java.lang.reflect.TypeVariable
+import java.sql.SQLException
+
+import static java.lang.annotation.ElementType.*
+import static java.lang.annotation.RetentionPolicy.RUNTIME
+
+@Target([PARAMETER, FIELD, METHOD, ANNOTATION_TYPE, TYPE_USE, LOCAL_VARIABLE])
+@Retention(RUNTIME)
+@interface JSR308 { }
+
+class JSR308BaseClass<T> {}
+interface JSR308Interface1<T> {}
+interface JSR308Interface2<T extends @JSR308 CharSequence> {}
+
+class JSR308Class extends @JSR308 JSR308BaseClass<@JSR308 List> implements @JSR308 JSR308Interface1<@JSR308 String>, @JSR308 JSR308Interface2<@JSR308 String> {
+ @JSR308 private String name;
+
+ @JSR308 List<@JSR308 String> test(@JSR308 List<@JSR308 ? extends @JSR308 Object> list) throws @JSR308 IOException, @JSR308 java.sql.SQLException {
+ @JSR308 List<@JSR308 String> localVar = new @JSR308 ArrayList<@JSR308 String>();
+
+ try {
+ for (e in list) {
+ String t = (@JSR308 String) e;
+ localVar.add(t);
+ }
+ } catch (@JSR308 Exception e) {
+ }
+
+ String @JSR308 [] strs = new String @JSR308 [] { 'a' }
+ String @JSR308 [] @JSR308 [] strs2 = new String @JSR308 [] @JSR308 [] { new String[] {'a', 'b'} }
+ String @JSR308 [] @JSR308 [] @JSR308 [] strs3 = new String @JSR308 [1] @JSR308 [2] @JSR308 []
+ String @JSR308 [] @JSR308 [] @JSR308 [] @JSR308 [] strs4 = new String @JSR308 [1] @JSR308 [2] @JSR308 [] @JSR308 []
+
+ localVar.add(strs[0])
+ localVar.add(strs2[0][1])
+ assert null != strs3
+ assert null != strs4
+
+ return localVar
+ }
+
+ void test2(@JSR308 JSR308Class this) {}
+}
+
+def jsr308Class = new JSR308Class();
+def list = new ArrayList<@JSR308 String>();
+list.addAll(["1", "2"]);
+def result = jsr308Class.test(list)
+assert ['1', '2', 'a', 'b'] == result
+
+assert 'JSR308BaseClass<java.util.List>' == JSR308Class.class.getAnnotatedSuperclass().type.typeName
+assert ['JSR308Interface1<java.lang.String>', 'JSR308Interface2<java.lang.String>'] == JSR308Class.class.getAnnotatedInterfaces().collect(e -> e.type.typeName)
+
+Method testMethod = JSR308Class.class.getDeclaredMethods().find(e -> e.name == 'test')
+assert [IOException, SQLException] == testMethod.getAnnotatedExceptionTypes().collect(e -> e.type)
+assert 'java.util.List<java.lang.String>' == testMethod.getAnnotatedReturnType().type.typeName
+assert ['java.util.List<?>'] == testMethod.getAnnotatedParameterTypes().collect(e -> e.type.typeName)
+
+Method test2Method = JSR308Class.class.getDeclaredMethods().find(e -> e.name == 'test2')
+assert JSR308Class.class == test2Method.getAnnotatedReceiverType().type
+
+Parameter listParameter = testMethod.getParameters()[0]
+assert 'java.util.List<?>' == listParameter.getAnnotatedType().type.typeName
+
+Field nameField = JSR308Class.class.getDeclaredField('name');
+assert String.class == nameField.getAnnotatedType().type
+
+TypeVariable tv = JSR308Interface2.class.getTypeParameters()[0]
+assert [CharSequence.class] == tv.getAnnotatedBounds().collect(e -> e.type)
[2/2] groovy git commit: Merge branch 'jsr308' of
https://git-wip-us.apache.org/repos/asf/groovy
Posted by su...@apache.org.
Merge branch 'jsr308' of https://git-wip-us.apache.org/repos/asf/groovy
Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/bc2f4eb5
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/bc2f4eb5
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/bc2f4eb5
Branch: refs/heads/master
Commit: bc2f4eb50a56b2d1a336734448f145f629fe4ab8
Parents: 05f1e28 c659f4a
Author: sunlan <su...@apache.org>
Authored: Sat Jun 24 10:48:56 2017 +0800
Committer: sunlan <su...@apache.org>
Committed: Sat Jun 24 10:48:56 2017 +0800
----------------------------------------------------------------------
.../apache/groovy/parser/antlr4/GroovyParser.g4 | 57 +++++--
.../apache/groovy/parser/antlr4/AstBuilder.java | 147 ++++++++++++++-----
.../parser/antlr4/GroovyParserTest.groovy | 4 +-
.../src/test/resources/bugs/GROOVY-8228.groovy | 92 ++++++++++++
4 files changed, 249 insertions(+), 51 deletions(-)
----------------------------------------------------------------------