From 5afa50f37351429e7e925fe1fabdaa68f9f03b61 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Sat, 3 Dec 2016 13:18:52 -0600 Subject: [PATCH] Fix issue #232 or #233: Class literals in annotation values w/o ".class" --- .../core/tests/basic/AnnotationsTests.java | 64 ++- .../core/tests/basic/GenericsTests.java | 29 +- .../ast/GroovyCompilationUnitDeclaration.java | 92 +-- .../compiler/ast/MemberValuePair.java | 534 ++++++++++-------- .../compiler/ast/MemberValuePair.java | 54 +- .../compiler/ast/MemberValuePair.java | 54 +- .../compiler/ast/MemberValuePair.java | 54 +- .../compiler/ast/MemberValuePair.java | 54 +- .../compiler/ast/MemberValuePair.java | 54 +- .../compiler/ast/MemberValuePair.java | 54 +- 10 files changed, 705 insertions(+), 338 deletions(-) diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/AnnotationsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/AnnotationsTests.java index f753e0ea2c..ea285e8b4f 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/AnnotationsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/AnnotationsTests.java @@ -77,6 +77,58 @@ public void testLongLiteral() { runConformTest(sources); } + public void testBigIntegerLiteral() { + String[] sources = { + "Min.java", + "import java.lang.annotation.*;\n" + + "@Target(ElementType.FIELD)\n" + + "@interface Min {\n" + + " long value();\n" + + "}", + + "Main.groovy", + "class Main {\n" + + " @Min(0G)\n" + + " Integer index\n" + + "}" + }; + + // there should not be an error from the Java model -- org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.UnitPopulator.createConstantExpression(ConstantExpression) + runNegativeTest(sources, + "----------\n" + + "1. ERROR in Main.groovy (at line 2)\n" + + "\t@Min(0G)\n" + + "\t ^" + (GroovyUtils.isAtLeastGroovy(20) ? "^" : "") + "\n" + + "Groovy:Attribute 'value' should have type 'java.lang.Long'; but found type 'java.math.BigInteger' in @Min\n" + + "----------\n"); + } + + public void testBigDecimalLiteral() { + String[] sources = { + "Min.java", + "import java.lang.annotation.*;\n" + + "@Target(ElementType.FIELD)\n" + + "@interface Min {\n" + + " double value();\n" + + "}", + + "Main.groovy", + "class Main {\n" + + " @Min(1.1G)\n" + + " BigDecimal index\n" + + "}" + }; + + // there should not be an error from the Java model -- org.codehaus.jdt.groovy.internal.compiler.ast.GroovyCompilationUnitDeclaration.UnitPopulator.createConstantExpression(ConstantExpression) + runNegativeTest(sources, + "----------\n" + + "1. ERROR in Main.groovy (at line 2)\n" + + "\t@Min(1.1G)\n" + + "\t ^" + (GroovyUtils.isAtLeastGroovy(20) ? "^^^" : "") + "\n" + + "Groovy:Attribute 'value' should have type 'java.lang.Double'; but found type 'java.math.BigDecimal' in @Min\n" + + "----------\n"); + } + public void testClassAnnotationValue() { String[] sources = { "Anno.java", @@ -172,16 +224,18 @@ public void testDoubleAttributeWithBigDecimalValue() { "AnnotationDoubleTest.groovy", "class AnnotationDoubleTest {\n" + - "class FooWithAnnotation { @AnnotationDouble(value=\"test\", width=1.0) double value; }\n" + - "def test = new AnnotationDoubleTest()\n" + + " class FooWithAnnotation {\n" + + " @AnnotationDouble(value='test', width=1.0) double value\n" + + " }\n" + + " def test = new AnnotationDoubleTest()\n" + "}" }; runNegativeTest(sources, "----------\n" + - "1. ERROR in AnnotationDoubleTest.groovy (at line 2)\n" + - "\tclass FooWithAnnotation { @AnnotationDouble(value=\"test\", width=1.0) double value; }\n" + - "\t ^" + (GroovyUtils.isAtLeastGroovy(20) ? "^^" : "") + "\n" + + "1. ERROR in AnnotationDoubleTest.groovy (at line 3)\n" + + "\t@AnnotationDouble(value='test', width=1.0) double value\n" + + "\t ^" + (GroovyUtils.isAtLeastGroovy(20) ? "^^" : "") + "\n" + "Groovy:Attribute 'width' should have type 'java.lang.Double'; but found type 'java.math.BigDecimal' in @AnnotationDouble\n" + "----------\n"); } diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java index af5b65f78a..daf54be0ec 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/basic/GenericsTests.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.core.tests.util.GroovyUtils; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; +import org.osgi.framework.Version; public final class GenericsTests extends AbstractGroovyRegressionTest { @@ -1609,14 +1610,20 @@ public void testHalfFinishedGenericsProgramWithSuppressionValueSpeltWrong() { public void testHalfFinishedGenericsProgramWithMultipleSuppressionValues() { String[] sources = { "Demo.groovy", - "public class Demo {\n"+ - "\n"+ - "@SuppressWarnings([\"rawtypes\",\"cast\"])\n"+ - "List myList;\n"+ + "class Demo {\n"+ + " @SuppressWarnings(['rawtypes','cast'])\n"+ + " List list\n"+ "}" }; - runNegativeTest(sources, ""); + // Eclipse Oxygen (i.e. JDT Core 3.13) added warning for mixed mode + Version v = Platform.getBundle("org.eclipse.jdt.core").getVersion(); + runNegativeTest(sources, (v.getMajor() == 3 && v.getMinor() < 13) ? "" : "----------\n" + + "1. WARNING in Demo.groovy (at line 2)\n" + + "\t@SuppressWarnings(['rawtypes','cast'])\n" + + "\t ^^^^^^\n" + + "At least one of the problems in category 'cast' is not analysed due to a compiler option being ignored\n" + + "----------\n"); } public void testHalfFinishedGenericsProgramWithMultipleSuppressionValuesWithOneSpeltWrong() { @@ -1638,12 +1645,8 @@ public void testHalfFinishedGenericsProgramWithMultipleSuppressionValuesWithOneS "----------\n"); } - private boolean isEclipse36() { - return Platform.getBundle("org.eclipse.jdt.core").getVersion().toString().startsWith("3.6"); - } - public void testJava7() { - if (isEclipse36() || complianceLevel < ClassFileConstants.JDK1_7) return; + if (complianceLevel < ClassFileConstants.JDK1_7) return; String[] sources = { "A.java", @@ -1668,8 +1671,7 @@ public void testJava7() { } public void testJava7_2() { - if (isEclipse36() || complianceLevel >= ClassFileConstants.JDK1_7) return; - // should fail if compliance level < 1.7 + if (complianceLevel >= ClassFileConstants.JDK1_7) return; String[] sources = { "A.java", @@ -1702,8 +1704,7 @@ public void testJava7_2() { } public void testJava7_3() { - if (isEclipse36() || complianceLevel >= ClassFileConstants.JDK1_7) return; - // should fail if compliance level < 1.7 + if (complianceLevel >= ClassFileConstants.JDK1_7) return; String[] sources = { "A.java", diff --git a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java index e9ea47a31b..84377736da 100644 --- a/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java +++ b/base/org.eclipse.jdt.groovy.core/src/org/codehaus/jdt/groovy/internal/compiler/ast/GroovyCompilationUnitDeclaration.java @@ -565,10 +565,8 @@ private void recordProblems(List errors) { eoffset = sourceEnd; } - char[] filename = getFileName(); - p = new DefaultProblemFactory().createProblem(filename, 0, new String[] { msg }, 0, new String[] { msg }, sev, soffset, - eoffset, line, scol); - this.problemReporter.record(p, compilationResult, this, false); + p = new DefaultProblemFactory().createProblem(getFileName(), 0, new String[] {msg}, 0, new String[] {msg}, sev, soffset, eoffset, line, scol); + problemReporter.record(p, compilationResult, this, false); errorsRecorded.add(message); log(String.valueOf(compilationResult.getFileName()) + ": " + line + " " + msg); } @@ -1605,10 +1603,10 @@ private org.eclipse.jdt.internal.compiler.ast.Expression createAnnotationMemberE } else if (expr instanceof PropertyExpression) { PropertyExpression prop = (PropertyExpression) expr; assert prop.getProperty() instanceof ConstantExpression; - String name = prop.getPropertyAsString(); - if (name.equals("class") || !NON_TYPE_NAME.matcher(name).matches()) { + if (prop.getPropertyAsString().equals("class")) { return new ClassLiteralAccess(expr.getEnd(), createTypeReferenceForClassLiteral(prop)); } + // could still be a class literal; Groovy does not require ".class" -- resolved in MemberValuePair char[][] tokens = CharOperation.splitOn('.', prop.getText().toCharArray()); // guess the intermediate positions based on start offset and token lengths int n = tokens.length, s = prop.getObjectExpression().getStart(); @@ -1623,12 +1621,8 @@ private org.eclipse.jdt.internal.compiler.ast.Expression createAnnotationMemberE } else if (expr instanceof VariableExpression) { String name = ((VariableExpression) expr).getName(); - if (NON_TYPE_NAME.matcher(name).matches()) { - // could be a class field or statically imported constant also... - return new SingleNameReference(name.toCharArray(), toPos(expr.getStart(), expr.getEnd() - 1)); - } - // likely a class literal; Groovy does not require ".class" - return new ClassLiteralAccess(expr.getEnd(), new SingleTypeReference(name.toCharArray(), toPos(expr.getStart(), expr.getEnd() - 1))); + // could be a class literal; Groovy does not require ".class" -- resolved in MemberValuePair + return new SingleNameReference(name.toCharArray(), toPos(expr.getStart(), expr.getEnd() - 1)); } else if (expr instanceof ClosureExpression) { // annotation is something like "@Tag(value = { some computation })" return "Closure.class" to appease JDT @@ -1642,9 +1636,6 @@ private org.eclipse.jdt.internal.compiler.ast.Expression createAnnotationMemberE return new NullLiteral(expr.getStart(), expr.getEnd()); } - // TODO: Look up name to determine if it is a class or a constant - private static final Pattern NON_TYPE_NAME = Pattern.compile("[a-z_]\\w*|[A-Z][A-Z_0-9]*"); - private org.eclipse.jdt.internal.compiler.ast.MemberValuePair[] createAnnotationMemberValuePairs(Map memberValuePairs) { List mvps = new ArrayList(memberValuePairs.size()); @@ -1653,7 +1644,7 @@ private org.eclipse.jdt.internal.compiler.ast.MemberValuePair[] createAnnotation char[] name = memberValuePair.getKey().toCharArray(); int start = memberValuePair.getValue().getStart() - name.length - 1, until = memberValuePair.getValue().getEnd(); org.eclipse.jdt.internal.compiler.ast.Expression value = createAnnotationMemberExpression(memberValuePair.getValue()); - mvps.add(new org.eclipse.jdt.internal.compiler.ast.MemberValuePair(name, start, until, value)); // TODO: set binding? + mvps.add(new org.eclipse.jdt.internal.compiler.ast.MemberValuePair(name, start, until, value)); } return mvps.toArray(new org.eclipse.jdt.internal.compiler.ast.MemberValuePair[mvps.size()]); @@ -1678,10 +1669,10 @@ private Literal createConstantExpression(ConstantExpression expr) { } else if (type.equals("int") || type.equals("java.lang.Integer")) { char[] chars = value.toString().toCharArray(); return IntLiteral.buildIntLiteral(chars, start, start + chars.length); - } else if (type.equals("long") || type.equals("java.lang.Long")) { + } else if (type.equals("long") || type.equals("java.lang.Long") || type.equals("java.math.BigInteger")) { char[] chars = (value.toString() + 'L').toCharArray(); return LongLiteral.buildLongLiteral(chars, start, start + chars.length); - } else if (type.equals("double") || type.equals("java.lang.Double")) { + } else if (type.equals("double") || type.equals("java.lang.Double") || type.equals("java.math.BigDecimal")) { return new DoubleLiteral(value.toString().toCharArray(), start, until); } else if (type.equals("float") || type.equals("java.lang.Float")) { return new FloatLiteral(value.toString().toCharArray(), start, until); @@ -1691,9 +1682,6 @@ private Literal createConstantExpression(ConstantExpression expr) { return IntLiteral.buildIntLiteral(chars, start, start + chars.length); } else if (type.equals("char") || type.equals("java.lang.Character")) { return new CharLiteral(value.toString().toCharArray(), start, until); - } else if (type.equals("java.math.BigDecimal")) { - // TODO: Return something else for BigDecimal? This is a bit of a cheat... - return new DoubleLiteral(value.toString().toCharArray(), start, until); } else { Util.log(IStatus.WARNING, "Unhandled annotation constant type: " + expr.getType().getName()); return null; @@ -1993,7 +1981,6 @@ private TypeParameter[] createTypeParametersForGenerics(GenericsType[] generics) // move past T offset += length; source = source.substring(length); - //if (source != null) { // move past "extends" and w.spaces Matcher m = EXTENDS.matcher(source); if (m.find()) { @@ -2001,33 +1988,13 @@ private TypeParameter[] createTypeParametersForGenerics(GenericsType[] generics) offset += length; source = source.substring(length); } - //else { - // while (Character.isWhitespace(source.charAt(0))) { - // offset += 1; - // source = source.substring(1); - // } - // if (source.startsWith("extends")) { - // offset += 7; - // source = source.substring(7); - // } - // while (Character.isWhitespace(source.charAt(0))) { - // offset += 1; - // source = source.substring(1); - // } - //} - //} else { offset += 9; } // ballpark it length = upperBounds[0].getName().length(); // TODO: Is this correct for qualified and unqualified occurrences? String name = GroovyUtils.splitName(upperBounds[0])[1]; assert length == source.indexOf(name) + name.length(); - //// check source for name to regain some accuracy - //int idx = source.indexOf(name); - //if (idx != -1) { - // length = idx + name.length(); - //} - // ???: Would a ClassNode with its own generics ever be missing sloc? + // Would a ClassNode with its own generics ever be missing sloc? assert source.length() == length || source.charAt(length) != '<'; } @@ -2043,35 +2010,17 @@ private TypeParameter[] createTypeParametersForGenerics(GenericsType[] generics) offset = _start; } else { // it appears only MetaClass or GeneratedClosure could get here... offset += length; - //if (source != null) { - // move past bounds type + // move past bounds type + source = source.substring(length); + // move past "&" and w.spaces + Matcher m = AND.matcher(source); + if (m.find()) { + length = m.group().length(); + offset += length; source = source.substring(length); - // move past "&" and w.spaces - Matcher m = AND.matcher(source); - if (m.find()) { - length = m.group().length(); - offset += length; - source = source.substring(length); - } - //else { - // char c; - // while ((c = source.charAt(0)) == '&' || Character.isWhitespace(c)) { - // offset += 1; - // source = source.substring(1); - // } - //} - //} else { offset += 3; } // ballpark it + } length = upperBounds[0].getName().length(); - // TODO: Is this correct for qualified and unqualified occurrences? - //String name = GroovyUtils.splitName(upperBounds[j])[1]; - //assert length == source.indexOf(name) + name.length(); - //int idx = source.indexOf(name); - //if (idx != -1) { - // length = idx + name.length(); - //} - // ???: Would a ClassNode with its own generics ever be missing sloc? - //assert source.length() == length || source.charAt(length) != '<'; } typeParameter.bounds[j - 1] = createTypeReferenceForClassNode(upperBounds[j], offset, offset + length); @@ -2521,9 +2470,8 @@ private void addUnlessDuplicate(List accumulatedDecla boolean argsTheSame = true; for (int i = 0; i < mdArgsLen; i++) { // FIXASC this comparison can fail if some are fully qualified and some not - in fact it - // suggests that default param variants should be built by augmentMethod() in a similar way to - // the GroovyObject methods, rather than during type declaration construction - but not super urgent right - // now + // suggests that default param variants should be built by augmentMethod() in a similar + // way to the GroovyObject methods, rather than during type declaration construction if (!CharOperation.equals(mdArgs[i].type.getTypeName(), vmdArgs[i].type.getTypeName())) { argsTheSame = false; break; diff --git a/jdt-patch/e37/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e37/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 07415d9a4c..679c99fd8f 100644 --- a/jdt-patch/e37/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e37/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,241 +1,293 @@ -/******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.jdt.internal.compiler.ast; - -import org.eclipse.jdt.internal.compiler.ASTVisitor; -import org.eclipse.jdt.internal.compiler.impl.Constant; -import org.eclipse.jdt.internal.compiler.lookup.Binding; -import org.eclipse.jdt.internal.compiler.lookup.BlockScope; -import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair; -import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; -import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; -import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; -import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; - -/** - * MemberValuePair node - */ -public class MemberValuePair extends ASTNode { - - public char[] name; - public Expression value; - public MethodBinding binding; - /** - * The representation of this pair in the type system. - */ - public ElementValuePair compilerElementPair = null; - - public MemberValuePair(char[] token, int sourceStart, int sourceEnd, Expression value) { - this.name = token; - this.sourceStart = sourceStart; - this.sourceEnd = sourceEnd; - this.value = value; - if (value instanceof ArrayInitializer) { - value.bits |= IsAnnotationDefaultValue; - } - } - - /* (non-Javadoc) - * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer) - */ - public StringBuffer print(int indent, StringBuffer output) { - output - .append(this.name) - .append(" = "); //$NON-NLS-1$ - this.value.print(0, output); - return output; - } - - public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { - - if (this.value == null) { - this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); - return; - } - if (requiredType == null) { - // fault tolerance: keep resolving - if (this.value instanceof ArrayInitializer) { - this.value.resolveTypeExpecting(scope, null); - } else { - this.value.resolveType(scope); - } - this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); - return; - } - - this.value.setExpectedType(requiredType); // needed in case of generic method invocation - TypeBinding valueType; - if (this.value instanceof ArrayInitializer) { - ArrayInitializer initializer = (ArrayInitializer) this.value; - valueType = initializer.resolveTypeExpecting(scope, this.binding.returnType); - } else if (this.value instanceof ArrayAllocationExpression) { - scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); - this.value.resolveType(scope); - valueType = null; // no need to pursue - } else { - valueType = this.value.resolveType(scope); - // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 - ASTVisitor visitor = new ASTVisitor() { - public boolean visit(SingleNameReference reference, BlockScope scop) { - if (reference.binding instanceof LocalVariableBinding) { - ((LocalVariableBinding) reference.binding).useFlag = LocalVariableBinding.USED; - } - return true; - } - }; - this.value.traverse(visitor, scope); - } - this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); - if (valueType == null) - return; - - TypeBinding leafType = requiredType.leafComponentType(); - if (!(this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType) - || valueType.isCompatibleWith(requiredType))) { - - if (!(requiredType.isArrayType() - && requiredType.dimensions() == 1 - && (this.value.isConstantValueOfTypeAssignableToType(valueType, leafType) - || valueType.isCompatibleWith(leafType)))) { - - if (leafType.isAnnotationType() && !valueType.isAnnotationType()) { - scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); - } else { - scope.problemReporter().typeMismatchError(valueType, requiredType, this.value, null); - } - return; // may allow to proceed to find more errors at once - } - } else { - scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(), valueType.leafComponentType()); - this.value.computeConversion(scope, requiredType, valueType); - } - - // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these - checkAnnotationMethodType: { - switch (leafType.erasure().id) { - case T_byte : - case T_short : - case T_char : - case T_int : - case T_long : - case T_float : - case T_double : - case T_boolean : - case T_JavaLangString : - if (this.value instanceof ArrayInitializer) { - ArrayInitializer initializer = (ArrayInitializer) this.value; - final Expression[] expressions = initializer.expressions; - if (expressions != null) { - for (int i =0, max = expressions.length; i < max; i++) { - Expression expression = expressions[i]; - if (expression.resolvedType == null) continue; // fault-tolerance - if (expression.constant == Constant.NotAConstant) { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, expressions[i], false); - } - } - } - } else if (this.value.constant == Constant.NotAConstant) { - if (valueType.isArrayType()) { - scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); - } else { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, false); - } - } - break checkAnnotationMethodType; - case T_JavaLangClass : - if (this.value instanceof ArrayInitializer) { - ArrayInitializer initializer = (ArrayInitializer) this.value; - final Expression[] expressions = initializer.expressions; - if (expressions != null) { - for (int i =0, max = expressions.length; i < max; i++) { - Expression currentExpression = expressions[i]; - if (!(currentExpression instanceof ClassLiteralAccess)) { - scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, currentExpression); - } - } - } - } else if (!(this.value instanceof ClassLiteralAccess)) { - scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, this.value); - } - break checkAnnotationMethodType; - } - if (leafType.isEnum()) { - if (this.value instanceof NullLiteral) { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); - } else if (this.value instanceof ArrayInitializer) { - ArrayInitializer initializer = (ArrayInitializer) this.value; - final Expression[] expressions = initializer.expressions; - if (expressions != null) { - for (int i =0, max = expressions.length; i < max; i++) { - Expression currentExpression = expressions[i]; - if (currentExpression instanceof NullLiteral) { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true); - } else if (currentExpression instanceof NameReference) { - NameReference nameReference = (NameReference) currentExpression; - final Binding nameReferenceBinding = nameReference.binding; - if (nameReferenceBinding.kind() == Binding.FIELD) { - FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding; - if (!fieldBinding.declaringClass.isEnum()) { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true); - } - } - } - } - } - } else if (this.value instanceof NameReference) { - NameReference nameReference = (NameReference) this.value; - final Binding nameReferenceBinding = nameReference.binding; - if (nameReferenceBinding.kind() == Binding.FIELD) { - FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding; - if (!fieldBinding.declaringClass.isEnum()) { - if (!fieldBinding.type.isArrayType()) { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); - } else { - scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); - } - } - } - } else { - scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); - } - break checkAnnotationMethodType; - } - if (leafType.isAnnotationType()) { - if (!valueType.leafComponentType().isAnnotationType()) { // check annotation type and also reject null literal - scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); - } else if (this.value instanceof ArrayInitializer) { - ArrayInitializer initializer = (ArrayInitializer) this.value; - final Expression[] expressions = initializer.expressions; - if (expressions != null) { - for (int i =0, max = expressions.length; i < max; i++) { - Expression currentExpression = expressions[i]; - if (currentExpression instanceof NullLiteral || !(currentExpression instanceof Annotation)) { - scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, currentExpression, leafType); - } - } - } - } else if (!(this.value instanceof Annotation)) { - scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); - } - break checkAnnotationMethodType; - } - } - } - - public void traverse(ASTVisitor visitor, BlockScope scope) { - if (visitor.visit(this, scope)) { - if (this.value != null) { - this.value.traverse(visitor, scope); - } - } - visitor.endVisit(this, scope); - } -} +// GROOVY PATCHED +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.compiler.ast; + +import java.util.Arrays; + +import org.eclipse.jdt.internal.compiler.ASTVisitor; +import org.eclipse.jdt.internal.compiler.impl.Constant; +import org.eclipse.jdt.internal.compiler.lookup.Binding; +import org.eclipse.jdt.internal.compiler.lookup.BlockScope; +import org.eclipse.jdt.internal.compiler.lookup.ElementValuePair; +import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; +import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; +import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; + +/** + * MemberValuePair node + */ +public class MemberValuePair extends ASTNode { + + public char[] name; + public Expression value; + public MethodBinding binding; + /** + * The representation of this pair in the type system. + */ + public ElementValuePair compilerElementPair = null; + + public MemberValuePair(char[] token, int sourceStart, int sourceEnd, Expression value) { + this.name = token; + this.sourceStart = sourceStart; + this.sourceEnd = sourceEnd; + this.value = value; + if (value instanceof ArrayInitializer) { + value.bits |= IsAnnotationDefaultValue; + } + } + + /* (non-Javadoc) + * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer) + */ + public StringBuffer print(int indent, StringBuffer output) { + output + .append(this.name) + .append(" = "); //$NON-NLS-1$ + this.value.print(0, output); + return output; + } + + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + + public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { + + if (this.value == null) { + this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); + return; + } + if (requiredType == null) { + // fault tolerance: keep resolving + if (this.value instanceof ArrayInitializer) { + this.value.resolveTypeExpecting(scope, null); + } else { + this.value.resolveType(scope); + } + this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); + return; + } + + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + + this.value.setExpectedType(requiredType); // needed in case of generic method invocation + TypeBinding valueType; + if (this.value instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) this.value; + valueType = initializer.resolveTypeExpecting(scope, this.binding.returnType); + } else if (this.value instanceof ArrayAllocationExpression) { + scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); + this.value.resolveType(scope); + valueType = null; // no need to pursue + } else { + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 + ASTVisitor visitor = new ASTVisitor() { + public boolean visit(SingleNameReference reference, BlockScope scop) { + if (reference.binding instanceof LocalVariableBinding) { + ((LocalVariableBinding) reference.binding).useFlag = LocalVariableBinding.USED; + } + return true; + } + }; + this.value.traverse(visitor, scope); + } + this.compilerElementPair = new ElementValuePair(this.name, this.value, this.binding); + if (valueType == null) + return; + + TypeBinding leafType = requiredType.leafComponentType(); + if (!(this.value.isConstantValueOfTypeAssignableToType(valueType, requiredType) + || valueType.isCompatibleWith(requiredType))) { + + if (!(requiredType.isArrayType() + && requiredType.dimensions() == 1 + && (this.value.isConstantValueOfTypeAssignableToType(valueType, leafType) + || valueType.isCompatibleWith(leafType)))) { + + if (leafType.isAnnotationType() && !valueType.isAnnotationType()) { + scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); + } else { + scope.problemReporter().typeMismatchError(valueType, requiredType, this.value, null); + } + return; // may allow to proceed to find more errors at once + } + } else { + scope.compilationUnitScope().recordTypeConversion(requiredType.leafComponentType(), valueType.leafComponentType()); + this.value.computeConversion(scope, requiredType, valueType); + } + + // annotation methods can only return base types, String, Class, enum type, annotation types and arrays of these + checkAnnotationMethodType: { + switch (leafType.erasure().id) { + case T_byte : + case T_short : + case T_char : + case T_int : + case T_long : + case T_float : + case T_double : + case T_boolean : + case T_JavaLangString : + if (this.value instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) this.value; + final Expression[] expressions = initializer.expressions; + if (expressions != null) { + for (int i =0, max = expressions.length; i < max; i++) { + Expression expression = expressions[i]; + if (expression.resolvedType == null) continue; // fault-tolerance + if (expression.constant == Constant.NotAConstant) { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, expressions[i], false); + } + } + } + } else if (this.value.constant == Constant.NotAConstant) { + if (valueType.isArrayType()) { + scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); + } else { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, false); + } + } + break checkAnnotationMethodType; + case T_JavaLangClass : + if (this.value instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) this.value; + final Expression[] expressions = initializer.expressions; + if (expressions != null) { + for (int i =0, max = expressions.length; i < max; i++) { + Expression currentExpression = expressions[i]; + if (!(currentExpression instanceof ClassLiteralAccess)) { + scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, currentExpression); + } + } + } + } else if (!(this.value instanceof ClassLiteralAccess)) { + scope.problemReporter().annotationValueMustBeClassLiteral(this.binding.declaringClass, this.name, this.value); + } + break checkAnnotationMethodType; + } + if (leafType.isEnum()) { + if (this.value instanceof NullLiteral) { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); + } else if (this.value instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) this.value; + final Expression[] expressions = initializer.expressions; + if (expressions != null) { + for (int i =0, max = expressions.length; i < max; i++) { + Expression currentExpression = expressions[i]; + if (currentExpression instanceof NullLiteral) { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true); + } else if (currentExpression instanceof NameReference) { + NameReference nameReference = (NameReference) currentExpression; + final Binding nameReferenceBinding = nameReference.binding; + if (nameReferenceBinding.kind() == Binding.FIELD) { + FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding; + if (!fieldBinding.declaringClass.isEnum()) { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, currentExpression, true); + } + } + } + } + } + } else if (this.value instanceof NameReference) { + NameReference nameReference = (NameReference) this.value; + final Binding nameReferenceBinding = nameReference.binding; + if (nameReferenceBinding.kind() == Binding.FIELD) { + FieldBinding fieldBinding = (FieldBinding) nameReferenceBinding; + if (!fieldBinding.declaringClass.isEnum()) { + if (!fieldBinding.type.isArrayType()) { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); + } else { + scope.problemReporter().annotationValueMustBeArrayInitializer(this.binding.declaringClass, this.name, this.value); + } + } + } + } else { + scope.problemReporter().annotationValueMustBeConstant(this.binding.declaringClass, this.name, this.value, true); + } + break checkAnnotationMethodType; + } + if (leafType.isAnnotationType()) { + if (!valueType.leafComponentType().isAnnotationType()) { // check annotation type and also reject null literal + scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); + } else if (this.value instanceof ArrayInitializer) { + ArrayInitializer initializer = (ArrayInitializer) this.value; + final Expression[] expressions = initializer.expressions; + if (expressions != null) { + for (int i =0, max = expressions.length; i < max; i++) { + Expression currentExpression = expressions[i]; + if (currentExpression instanceof NullLiteral || !(currentExpression instanceof Annotation)) { + scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, currentExpression, leafType); + } + } + } + } else if (!(this.value instanceof Annotation)) { + scope.problemReporter().annotationValueMustBeAnnotation(this.binding.declaringClass, this.name, this.value, leafType); + } + break checkAnnotationMethodType; + } + } + } + + public void traverse(ASTVisitor visitor, BlockScope scope) { + if (visitor.visit(this, scope)) { + if (this.value != null) { + this.value.traverse(visitor, scope); + } + } + visitor.endVisit(this, scope); + } +} diff --git a/jdt-patch/e42/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e42/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index e0f7cfb01c..55f0d2780f 100644 --- a/jdt-patch/e42/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e42/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -13,6 +14,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -21,6 +24,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -57,6 +61,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { if (this.value == null) { @@ -74,6 +108,21 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -84,7 +133,10 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) { diff --git a/jdt-patch/e43/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e43/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index e0f7cfb01c..55f0d2780f 100644 --- a/jdt-patch/e43/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e43/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -13,6 +14,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -21,6 +24,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -57,6 +61,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { if (this.value == null) { @@ -74,6 +108,21 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -84,7 +133,10 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) { diff --git a/jdt-patch/e43j8/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e43j8/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 103812bf42..3505715834 100644 --- a/jdt-patch/e43j8/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e43j8/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2013 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -13,6 +14,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -22,6 +25,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -58,6 +62,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { if (this.value == null) { @@ -75,6 +109,21 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ??? TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -85,7 +134,10 @@ public void resolveTypeExpecting(BlockScope scope, TypeBinding requiredType) { this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) { diff --git a/jdt-patch/e44/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e44/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 81ca5c886b..d2ee7d6022 100644 --- a/jdt-patch/e44/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e44/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -14,6 +15,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -23,6 +26,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -59,6 +63,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requiredType) { if (this.value == null) { @@ -76,6 +110,21 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ??? final TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -86,7 +135,10 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) { diff --git a/jdt-patch/e45/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e45/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 81ca5c886b..d2ee7d6022 100644 --- a/jdt-patch/e45/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e45/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -14,6 +15,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -23,6 +26,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -59,6 +63,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requiredType) { if (this.value == null) { @@ -76,6 +110,21 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ??? final TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -86,7 +135,10 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) { diff --git a/jdt-patch/e46/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java b/jdt-patch/e46/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java index 81ca5c886b..d2ee7d6022 100644 --- a/jdt-patch/e46/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java +++ b/jdt-patch/e46/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/MemberValuePair.java @@ -1,3 +1,4 @@ +// GROOVY PATCHED /******************************************************************************* * Copyright (c) 2000, 2014 IBM Corporation and others. * All rights reserved. This program and the accompanying materials @@ -14,6 +15,8 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import java.util.Arrays; + import org.eclipse.jdt.internal.compiler.ASTVisitor; import org.eclipse.jdt.internal.compiler.impl.Constant; import org.eclipse.jdt.internal.compiler.lookup.Binding; @@ -23,6 +26,7 @@ import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; +import org.eclipse.jdt.internal.compiler.lookup.Scope; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; /** @@ -59,6 +63,36 @@ public StringBuffer print(int indent, StringBuffer output) { return output; } + // GROOVY add + private boolean isGroovy(Scope scope) { + while (scope.parent != null) { + scope = scope.parent; + } + return scope.getClass().getSimpleName().startsWith("Groovy"); //$NON-NLS-1$ + } + + private Expression repairClassLiteralReference(Expression exp, BlockScope scope, TypeBinding[] valueType) { + TypeBinding vtb = null; + if (exp instanceof SingleNameReference) { + vtb = exp.resolveType(scope); + SingleNameReference ref = (SingleNameReference) exp; + if (vtb != null && Arrays.equals(ref.token, vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new SingleTypeReference(ref.token, ((long) ref.sourceStart) << 32 | ref.sourceEnd)); + } + } else if (this.value instanceof QualifiedNameReference) { + vtb = exp.resolveType(scope); + QualifiedNameReference ref = (QualifiedNameReference) exp; + if (vtb != null && Arrays.equals(ref.tokens[ref.tokens.length - 1], vtb.sourceName())) { + return new ClassLiteralAccess(ref.sourceEnd, new QualifiedTypeReference(ref.tokens, ref.sourcePositions)); + } + } + if (valueType != null) { + valueType[0] = vtb; + } + return exp; + } + // GROOVY end + public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requiredType) { if (this.value == null) { @@ -76,6 +110,21 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi return; } + // GROOVY add - handling for class literals that do not end in '.class' + TypeBinding[] vtb = null; + if (requiredType.isClass() || (requiredType.isArrayType() && requiredType.leafComponentType().isClass()) && isGroovy(scope)) { + if (this.value instanceof ArrayInitializer) { + Expression[] values = ((ArrayInitializer) this.value).expressions; + for (int i = 0, n = values.length; i < n; i += 1) { + values[i] = repairClassLiteralReference(values[i], scope, null); + } + } else { + vtb = new TypeBinding[1]; // need resolved type if value is resolved but unchanged + this.value = repairClassLiteralReference(this.value, scope, vtb); + } + } + // GROOVY end + this.value.setExpectedType(requiredType); // needed in case of generic method invocation - looks suspect, generic method invocation here ??? final TypeBinding valueType; if (this.value instanceof ArrayInitializer) { @@ -86,7 +135,10 @@ public void resolveTypeExpecting(final BlockScope scope, final TypeBinding requi this.value.resolveType(scope); valueType = null; // no need to pursue } else { - valueType = this.value.resolveType(scope); + // GROOVY edit -- returns null if called 2x + //valueType = this.value.resolveType(scope); + valueType = (vtb != null && vtb[0] != null) ? vtb[0] : this.value.resolveType(scope); + // GROOVY end // https://bugs.eclipse.org/bugs/show_bug.cgi?id=248897 ASTVisitor visitor = new ASTVisitor() { public boolean visit(SingleNameReference reference, BlockScope scop) {