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 <[email protected]> Authored: Tue Jun 20 22:08:55 2017 +0800 Committer: sunlan <[email protected]> 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)
