Merge branch 'master' into issue-2755
This commit is contained in:
@ -4,10 +4,9 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.types;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
@ -42,10 +41,10 @@ public final class TypeTestUtil {
|
||||
* if the type of the node is parameterized. Examples:
|
||||
*
|
||||
* <pre>{@code
|
||||
* isA(<new ArrayList<String>()>, List.class) = true
|
||||
* isA(<new ArrayList<String>()>, ArrayList.class) = true
|
||||
* isA(<new int[0]>, int[].class) = true
|
||||
* isA(<new String[0]>, Object[].class) = true
|
||||
* isA(List.class, <new ArrayList<String>()>) = true
|
||||
* isA(ArrayList.class, <new ArrayList<String>()>) = true
|
||||
* isA(int[].class, <new int[0]>) = true
|
||||
* isA(Object[].class, <new String[0]>) = true
|
||||
* isA(_, null) = false
|
||||
* isA(null, _) = NullPointerException
|
||||
* }</pre>
|
||||
@ -65,8 +64,11 @@ public final class TypeTestUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
return canBeExtended(clazz) ? isA(clazz.getName(), node)
|
||||
: isExactlyA(clazz, node);
|
||||
if (hasNoSubtypes(clazz)) {
|
||||
return isExactlyA(clazz, node);
|
||||
}
|
||||
String canoName = clazz.getCanonicalName();
|
||||
return canoName != null && isA(canoName, node);
|
||||
}
|
||||
|
||||
|
||||
@ -76,10 +78,10 @@ public final class TypeTestUtil {
|
||||
* if the type of the node is parameterized. Examples:
|
||||
*
|
||||
* <pre>{@code
|
||||
* isA(<new ArrayList<String>()>, "java.util.List") = true
|
||||
* isA(<new ArrayList<String>()>, "java.util.ArrayList") = true
|
||||
* isA(<new int[0]>, "int[]") = true
|
||||
* isA(<new String[0]>, "java.lang.Object[]") = true
|
||||
* isA("java.util.List", <new ArrayList<String>()>) = true
|
||||
* isA("java.util.ArrayList", <new ArrayList<String>()>) = true
|
||||
* isA("int[]", <new int[0]>) = true
|
||||
* isA("java.lang.Object[]", <new String[0]>) = true
|
||||
* isA(_, null) = false
|
||||
* isA(null, _) = NullPointerException
|
||||
* }</pre>
|
||||
@ -156,10 +158,10 @@ public final class TypeTestUtil {
|
||||
* if the type of the node is parameterized.
|
||||
*
|
||||
* <pre>{@code
|
||||
* isExactlyA(<new ArrayList<String>()>, List.class) = false
|
||||
* isExactlyA(<new ArrayList<String>()>, ArrayList.class) = true
|
||||
* isExactlyA(<new int[0]>, int[].class) = true
|
||||
* isExactlyA(<new String[0]>, Object[].class) = false
|
||||
* isExactlyA(List.class, <new ArrayList<String>()>) = false
|
||||
* isExactlyA(ArrayList.class, <new ArrayList<String>()>) = true
|
||||
* isExactlyA(int[].class, <new int[0]>) = true
|
||||
* isExactlyA(Object[].class, <new String[0]>) = false
|
||||
* isExactlyA(_, null) = false
|
||||
* isExactlyA(null, _) = NullPointerException
|
||||
* }</pre>
|
||||
@ -198,9 +200,12 @@ public final class TypeTestUtil {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canBeExtended(Class<?> clazz) {
|
||||
|
||||
private static boolean hasNoSubtypes(Class<?> clazz) {
|
||||
// Neither final nor an annotation. Enums & records have ACC_FINAL
|
||||
return (clazz.getModifiers() & (Opcodes.ACC_ANNOTATION | Opcodes.ACC_FINAL)) == 0;
|
||||
// Note: arrays have ACC_FINAL, but have subtypes by covariance
|
||||
// Note: annotations may be implemented by classes
|
||||
return Modifier.isFinal(clazz.getModifiers()) && !clazz.isArray();
|
||||
}
|
||||
|
||||
// those fallbacks can be removed with the newer symbol resolution,
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.types;
|
||||
|
||||
import java.io.ObjectStreamField;
|
||||
import java.io.Serializable;
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.concurrent.Callable;
|
||||
@ -20,6 +21,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTName;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTType;
|
||||
import net.sourceforge.pmd.lang.java.ast.TypeNode;
|
||||
import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
|
||||
import net.sourceforge.pmd.lang.java.types.testdata.SomeClassWithAnon;
|
||||
@ -56,12 +58,60 @@ public class TypeTestUtilTest extends BaseNonParserTest {
|
||||
|
||||
Assert.assertNull(klass.getType());
|
||||
Assert.assertTrue(TypeTestUtil.isA("org.FooBar", klass));
|
||||
assertIsA(klass, Iterable.class);
|
||||
assertIsA(klass, Enum.class);
|
||||
assertIsA(klass, Serializable.class);
|
||||
assertIsA(klass, Object.class);
|
||||
assertIsStrictSubtype(klass, Iterable.class);
|
||||
assertIsStrictSubtype(klass, Enum.class);
|
||||
assertIsStrictSubtype(klass, Serializable.class);
|
||||
assertIsStrictSubtype(klass, Object.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsAnArrayClass() {
|
||||
|
||||
ASTType arrayT =
|
||||
java.parse("import java.io.ObjectStreamField; "
|
||||
+ "class Foo { private static final ObjectStreamField[] serialPersistentFields; }")
|
||||
.getFirstDescendantOfType(ASTType.class);
|
||||
|
||||
|
||||
assertIsExactlyA(arrayT, ObjectStreamField[].class);
|
||||
assertIsStrictSubtype(arrayT, Object[].class);
|
||||
assertIsStrictSubtype(arrayT, Serializable.class);
|
||||
assertIsNot(arrayT, Serializable[].class);
|
||||
assertIsStrictSubtype(arrayT, Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAnAnnotationClass() {
|
||||
|
||||
ASTType arrayT =
|
||||
java.parse("class Foo { org.junit.Test field; }")
|
||||
.getFirstDescendantOfType(ASTType.class);
|
||||
|
||||
|
||||
assertIsExactlyA(arrayT, Test.class);
|
||||
assertIsStrictSubtype(arrayT, Annotation.class);
|
||||
assertIsStrictSubtype(arrayT, Object.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsAPrimitiveArrayClass() {
|
||||
|
||||
ASTType arrayT =
|
||||
java.parse("import java.io.ObjectStreamField; "
|
||||
+ "class Foo { private static final int[] serialPersistentFields; }")
|
||||
.getFirstDescendantOfType(ASTType.class);
|
||||
|
||||
|
||||
assertIsExactlyA(arrayT, int[].class);
|
||||
assertIsNot(arrayT, long[].class);
|
||||
assertIsNot(arrayT, Object[].class);
|
||||
|
||||
assertIsStrictSubtype(arrayT, Serializable.class);
|
||||
assertIsStrictSubtype(arrayT, Object.class);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testIsAFallbackAnnotation() {
|
||||
|
||||
@ -161,10 +211,38 @@ public class TypeTestUtilTest extends BaseNonParserTest {
|
||||
}
|
||||
|
||||
private void assertIsA(TypeNode node, Class<?> type) {
|
||||
Assert.assertTrue("TypeTestUtil::isA with class arg: " + type.getCanonicalName(),
|
||||
TypeTestUtil.isA(type, node));
|
||||
Assert.assertTrue("TypeTestUtil::isA with string arg: " + type.getCanonicalName(),
|
||||
TypeTestUtil.isA(type.getCanonicalName(), node));
|
||||
assertIsA(node, type, false, true);
|
||||
}
|
||||
|
||||
private void assertIsExactlyA(TypeNode node, Class<?> type) {
|
||||
assertIsA(node, type, true, true);
|
||||
assertIsA(node, type, false, true);
|
||||
}
|
||||
|
||||
private void assertIsNot(TypeNode node, Class<?> type) {
|
||||
assertIsA(node, type, true, false);
|
||||
assertIsA(node, type, false, false);
|
||||
}
|
||||
|
||||
private void assertIsNotExactly(TypeNode node, Class<?> type) {
|
||||
assertIsA(node, type, true, false);
|
||||
}
|
||||
|
||||
private void assertIsStrictSubtype(TypeNode node, Class<?> type) {
|
||||
assertIsNotExactly(node, type);
|
||||
assertIsA(node, type);
|
||||
}
|
||||
|
||||
private void assertIsA(TypeNode node, Class<?> type, boolean exactly, boolean expectTrue) {
|
||||
Assert.assertEquals("TypeTestUtil::isA with class arg: " + type.getCanonicalName(),
|
||||
expectTrue,
|
||||
exactly ? TypeTestUtil.isExactlyA(type, node)
|
||||
: TypeTestUtil.isA(type, node));
|
||||
Assert.assertEquals("TypeTestUtil::isA with string arg: " + type.getCanonicalName(),
|
||||
expectTrue,
|
||||
exactly ? TypeTestUtil.isExactlyA(type.getCanonicalName(), node)
|
||||
: TypeTestUtil.isA(type.getCanonicalName(), node));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user