diff --git a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java index 873697e422..a2d187c631 100644 --- a/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java +++ b/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/types/TypeTestUtil.java @@ -106,7 +106,11 @@ public final class TypeTestUtil { final Class clazz = loadClassWithNodeClassloader(node, canonicalName); + if (clazz != null) { + if (clazz.getCanonicalName() == null) { + return false; // no canonical name, give up: we shouldn't be able to access them + } return clazz.isAssignableFrom(nodeType); } else { return fallbackIsA(node, canonicalName, true); @@ -174,8 +178,16 @@ public final class TypeTestUtil { } - return node.getType() == null ? fallbackIsA(node, canonicalName, false) - : node.getType().getCanonicalName().equals(canonicalName); + if (node.getType() == null) { + return fallbackIsA(node, canonicalName, false); + } + + String canoname = node.getType().getCanonicalName(); + if (canoname == null) { + // anonymous/local class, or class nested within one of those + return false; + } + return canoname.equals(canonicalName); } diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java index a04149a88c..d672afc702 100644 --- a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/TypeTestUtilTest.java @@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.types; import java.io.Serializable; import java.lang.annotation.Annotation; +import java.util.concurrent.Callable; import org.junit.Assert; import org.junit.Rule; @@ -13,6 +14,7 @@ import org.junit.Test; import org.junit.function.ThrowingRunnable; import org.junit.rules.ExpectedException; +import net.sourceforge.pmd.lang.java.ast.ASTAllocationExpression; import net.sourceforge.pmd.lang.java.ast.ASTAnnotationTypeDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; @@ -20,6 +22,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTMarkerAnnotation; import net.sourceforge.pmd.lang.java.ast.ASTName; 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; public class TypeTestUtilTest extends BaseNonParserTest { @@ -74,6 +77,31 @@ public class TypeTestUtilTest extends BaseNonParserTest { assertIsA(klass, Object.class); } + @Test + public void testAnonClassTypeNPE() { + // #2756 + + ASTAllocationExpression anon = + java.parseClass(SomeClassWithAnon.class) + .getFirstDescendantOfType(ASTAllocationExpression.class); + + + Assert.assertNotNull("Type should be resolved", anon.getType()); + Assert.assertTrue("Anon class", anon.isAnonymousClass()); + Assert.assertTrue("Anon class", anon.getType().isAnonymousClass()); + Assert.assertTrue("Should be a Runnable", TypeTestUtil.isA(Runnable.class, anon)); + + // This is not a canonical name, so we give up early + Assert.assertFalse(TypeTestUtil.isA(SomeClassWithAnon.class.getName() + "$1", anon)); + Assert.assertFalse(TypeTestUtil.isExactlyA(SomeClassWithAnon.class.getName() + "$1", anon)); + + // this is the failure case: if the binary name doesn't match, we test the canoname, which was null + Assert.assertFalse(TypeTestUtil.isA(Callable.class, anon)); + Assert.assertFalse(TypeTestUtil.isA(Callable.class.getCanonicalName(), anon)); + Assert.assertFalse(TypeTestUtil.isExactlyA(Callable.class, anon)); + Assert.assertFalse(TypeTestUtil.isExactlyA(Callable.class.getCanonicalName(), anon)); + } + /** * If we don't have the annotation on the classpath, * we should resolve the full name via the import, if possible diff --git a/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/SomeClassWithAnon.java b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/SomeClassWithAnon.java new file mode 100644 index 0000000000..09d2467f25 --- /dev/null +++ b/pmd-java/src/test/java/net/sourceforge/pmd/lang/java/types/testdata/SomeClassWithAnon.java @@ -0,0 +1,25 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.java.types.testdata; + +/** + * #2756 + */ +public class SomeClassWithAnon { + + { + new Runnable() { + + @Override + public void run() { + + } + }; + + + } + + +}