GROOVY-9270: add checks for invalid instanceof reference types
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.20.2
diff --git a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
index 14847e7..7d476b7 100644
--- a/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
+++ b/src/main/java/org/codehaus/groovy/control/CompilationUnit.java
@@ -802,6 +802,14 @@
visitor = new LabelVerifier(source);
visitor.visitClass(classNode);
+ visitor = new InstanceOfVerifier() {
+ @Override
+ protected SourceUnit getSourceUnit() {
+ return source;
+ }
+ };
+ visitor.visitClass(classNode);
+
visitor = new ClassCompletionVerifier(source);
visitor.visitClass(classNode);
diff --git a/src/main/java/org/codehaus/groovy/control/InstanceOfVerifier.java b/src/main/java/org/codehaus/groovy/control/InstanceOfVerifier.java
new file mode 100644
index 0000000..8eacaf2
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/InstanceOfVerifier.java
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ */
+package org.codehaus.groovy.control;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.syntax.Types;
+
+public abstract class InstanceOfVerifier extends ClassCodeVisitorSupport {
+
+ @Override
+ public void visitBinaryExpression(BinaryExpression expression) {
+ if (expression.getOperation().isA(Types.INSTANCEOF_OPERATOR) &&
+ expression.getRightExpression() instanceof ClassExpression) {
+ ClassNode referenceType = expression.getRightExpression().getType();
+
+ if (ClassHelper.isPrimitiveType(referenceType)) {
+ addTypeError(expression.getRightExpression(), "primitive type " + referenceType.getName());
+ } else {
+ while (referenceType.isArray()) {
+ referenceType = referenceType.getComponentType();
+ }
+
+ if (referenceType.isGenericsPlaceHolder()) {
+ addTypeError(expression.getRightExpression(), "type parameter " + referenceType.getUnresolvedName() +
+ ". Use its erasure " + referenceType.getNameWithoutPackage() + " instead since further generic type information will be erased at runtime");
+ } else if (referenceType.getGenericsTypes() != null) {
+ // TODO: Cannot perform instanceof check against parameterized type Class<Type>. Use the form Class<?> instead since further eneric type information will be erased at runtime
+ }
+ }
+ }
+ super.visitBinaryExpression(expression);
+ }
+
+ private void addTypeError(Expression referenceExpr, String referenceType) {
+ addError("Cannot perform instanceof check against " + referenceType, referenceExpr);
+ }
+}
diff --git a/src/main/java/org/codehaus/groovy/syntax/Types.java b/src/main/java/org/codehaus/groovy/syntax/Types.java
index 26e12b9..0464dc6 100644
--- a/src/main/java/org/codehaus/groovy/syntax/Types.java
+++ b/src/main/java/org/codehaus/groovy/syntax/Types.java
@@ -304,6 +304,7 @@
public static final int REGEX_COMPARISON_OPERATOR = 1105; // =~, etc.
public static final int DEREFERENCE_OPERATOR = 1106; // ., ->
public static final int BITWISE_OPERATOR = 1107; // |, &, <<, >>, >>>, ^, ~
+ public static final int INSTANCEOF_OPERATOR = 1108; // instanceof, !instanceof
public static final int PREFIX_OPERATOR = 1200; // ++, !, etc.
public static final int POSTFIX_OPERATOR = 1210; // ++, etc.
@@ -414,6 +415,9 @@
case COMPARISON_OPERATOR:
return specific >= COMPARE_NOT_EQUAL && specific <= COMPARE_TO;
+ case INSTANCEOF_OPERATOR:
+ return specific == KEYWORD_INSTANCEOF || specific == COMPARE_NOT_INSTANCEOF;
+
case MATH_OPERATOR:
return (specific >= PLUS && specific <= RIGHT_SHIFT_UNSIGNED) || (specific >= NOT && specific <= LOGICAL_AND)
|| (specific >= BITWISE_OR && specific <= BITWISE_XOR);
diff --git a/src/test/groovy/bugs/Groovy9270.groovy b/src/test/groovy/bugs/Groovy9270.groovy
new file mode 100644
index 0000000..c4c794a
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9270.groovy
@@ -0,0 +1,96 @@
+/*
+ * 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.
+ */
+package groovy.bugs
+
+import groovy.transform.CompileStatic
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
+
+@CompileStatic
+final class Groovy9270 {
+
+ @Test
+ void testInstanceOfPrimitive1() {
+ def err = shouldFail '''
+ void meth(obj) {
+ if (obj instanceof int) {
+ // ...
+ }
+ }
+ '''
+
+ assert err =~ / Cannot perform instanceof check against primitive type int/
+ }
+
+ @Test
+ void testInstanceOfPrimitive2() {
+ def err = shouldFail '''
+ void meth(obj) {
+ if (obj !instanceof int) {
+ // ...
+ }
+ }
+ '''
+
+ assert err =~ / Cannot perform instanceof check against primitive type int/
+ }
+
+ @Test
+ void testInstanceOfPrimitiveArray() {
+ assertScript '''
+ void meth(obj) {
+ if (obj instanceof double[]) {
+ // ...
+ }
+ }
+ '''
+ }
+
+ @Test
+ void testInstanceOfTypeParameter1() {
+ def err = shouldFail '''
+ class C<T extends Number> {
+ void meth(obj) {
+ if (obj instanceof T) {
+ // ...
+ }
+ }
+ }
+ '''
+
+ assert err =~ / Cannot perform instanceof check against type parameter T/
+ }
+
+ @Test
+ void testInstanceOfTypeParameter2() {
+ def err = shouldFail '''
+ class C<T> {
+ void meth(obj) {
+ if (obj instanceof T[]) {
+ // ...
+ }
+ }
+ }
+ '''
+
+ assert err =~ / Cannot perform instanceof check against type parameter T/
+ }
+}