Merge branch 'pr-2771' into master

[java] Fix #2756, NPE in TypeTestUtil #2771
This commit is contained in:
Andreas Dangel
2020-09-11 17:40:43 +02:00
4 changed files with 68 additions and 2 deletions

View File

@ -19,6 +19,7 @@ This is a {{ site.pmd.release_type }} release.
* pmd-java * pmd-java
* [#2708](https://github.com/pmd/pmd/issues/2708): \[java] False positive FinalFieldCouldBeStatic when using lombok Builder.Default * [#2708](https://github.com/pmd/pmd/issues/2708): \[java] False positive FinalFieldCouldBeStatic when using lombok Builder.Default
* [#2738](https://github.com/pmd/pmd/issues/2738): \[java] Custom rule with @ExhaustiveEnumSwitch throws NPE * [#2738](https://github.com/pmd/pmd/issues/2738): \[java] Custom rule with @ExhaustiveEnumSwitch throws NPE
* [#2756](https://github.com/pmd/pmd/issues/2756): \[java] TypeTestUtil fails with NPE for anonymous class
### API Changes ### API Changes

View File

@ -106,7 +106,11 @@ public final class TypeTestUtil {
final Class<?> clazz = loadClassWithNodeClassloader(node, canonicalName); final Class<?> clazz = loadClassWithNodeClassloader(node, canonicalName);
if (clazz != null) { 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); return clazz.isAssignableFrom(nodeType);
} else { } else {
return fallbackIsA(node, canonicalName, true); return fallbackIsA(node, canonicalName, true);
@ -174,8 +178,16 @@ public final class TypeTestUtil {
} }
return node.getType() == null ? fallbackIsA(node, canonicalName, false) if (node.getType() == null) {
: node.getType().getCanonicalName().equals(canonicalName); 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);
} }

View File

@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.types;
import java.io.Serializable; import java.io.Serializable;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.util.concurrent.Callable;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
@ -13,6 +14,7 @@ import org.junit.Test;
import org.junit.function.ThrowingRunnable; import org.junit.function.ThrowingRunnable;
import org.junit.rules.ExpectedException; 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.ASTAnnotationTypeDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration; import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration; 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.ASTName;
import net.sourceforge.pmd.lang.java.ast.TypeNode; import net.sourceforge.pmd.lang.java.ast.TypeNode;
import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest; import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
import net.sourceforge.pmd.lang.java.types.testdata.SomeClassWithAnon;
public class TypeTestUtilTest extends BaseNonParserTest { public class TypeTestUtilTest extends BaseNonParserTest {
@ -74,6 +77,31 @@ public class TypeTestUtilTest extends BaseNonParserTest {
assertIsA(klass, Object.class); 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, * If we don't have the annotation on the classpath,
* we should resolve the full name via the import, if possible * we should resolve the full name via the import, if possible

View File

@ -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() {
}
};
}
}