[java] Fix typehelper fallback for simple class name
If there is no auxclasspath, then we still can use imports to check the full name before we fallback to simple name only. Also improve RuleTst to actually test without auxclasspath
This commit is contained in:
@ -6,6 +6,7 @@ package net.sourceforge.pmd.lang.java.typeresolution;
|
|||||||
|
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@ -16,6 +17,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
|||||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
|
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
|
||||||
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
import net.sourceforge.pmd.lang.java.ast.ASTEnumDeclaration;
|
||||||
import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
|
import net.sourceforge.pmd.lang.java.ast.ASTImplementsList;
|
||||||
|
import net.sourceforge.pmd.lang.java.ast.ASTImportDeclaration;
|
||||||
import net.sourceforge.pmd.lang.java.ast.TypeNode;
|
import net.sourceforge.pmd.lang.java.ast.TypeNode;
|
||||||
import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration;
|
import net.sourceforge.pmd.lang.java.symboltable.TypedNameDeclaration;
|
||||||
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader;
|
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader;
|
||||||
@ -85,6 +87,18 @@ public final class TypeHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static boolean fallbackIsA(TypeNode n, String clazzName) {
|
private static boolean fallbackIsA(TypeNode n, String clazzName) {
|
||||||
|
if (n.getImage() != null && !n.getImage().contains(".")) {
|
||||||
|
// simple name detected, check the imports to get the full name and use that for fallback
|
||||||
|
List<ASTImportDeclaration> imports = n.getRoot().findChildrenOfType(ASTImportDeclaration.class);
|
||||||
|
for (ASTImportDeclaration importDecl : imports) {
|
||||||
|
if (n.hasImageEqualTo(importDecl.getImportedSimpleName())) {
|
||||||
|
// found the import, compare the full names
|
||||||
|
return clazzName.equals(importDecl.getImportedName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back on using the simple name of the class only
|
||||||
if (clazzName.equals(n.getImage()) || clazzName.endsWith("." + n.getImage())) {
|
if (clazzName.equals(n.getImage()) || clazzName.endsWith("." + n.getImage())) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,8 @@ import org.junit.rules.ExpectedException;
|
|||||||
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;
|
||||||
|
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.ast.TypeNode;
|
||||||
import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
|
import net.sourceforge.pmd.lang.java.symboltable.BaseNonParserTest;
|
||||||
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader;
|
import net.sourceforge.pmd.lang.java.typeresolution.internal.NullableClassLoader;
|
||||||
@ -78,6 +80,22 @@ public class TypeHelperTest extends BaseNonParserTest {
|
|||||||
assertIsA(klass, Object.class);
|
assertIsA(klass, Object.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If we don't have the annotation on the classpath,
|
||||||
|
* we should resolve the full name via the import, if possible
|
||||||
|
* and compare then. Only after that, we should compare the
|
||||||
|
* simple names.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testIsAFallbackAnnotationSimpleNameImport() {
|
||||||
|
ASTName annotation = java.parse("package org; import foo.Stuff; @Stuff public class FooBar {}")
|
||||||
|
.getFirstDescendantOfType(ASTMarkerAnnotation.class).getFirstChildOfType(ASTName.class);
|
||||||
|
|
||||||
|
Assert.assertNull(annotation.getType());
|
||||||
|
Assert.assertTrue(TypeHelper.isA(annotation, "foo.Stuff"));
|
||||||
|
Assert.assertFalse(TypeHelper.isA(annotation, "other.Stuff"));
|
||||||
|
}
|
||||||
|
|
||||||
private void assertIsA(TypeNode node, Class<?> type) {
|
private void assertIsA(TypeNode node, Class<?> type) {
|
||||||
Assert.assertTrue("TypeHelper::isA with class arg: " + type.getCanonicalName(),
|
Assert.assertTrue("TypeHelper::isA with class arg: " + type.getCanonicalName(),
|
||||||
TypeHelper.isA(node, type));
|
TypeHelper.isA(node, type));
|
||||||
|
@ -150,41 +150,63 @@ public class MethodNamingConventions implements SomeInterface {
|
|||||||
]]>
|
]]>
|
||||||
</code>
|
</code>
|
||||||
</test-code>
|
</test-code>
|
||||||
|
|
||||||
<test-code>
|
<test-code>
|
||||||
<description>JUnit 4 test detection</description>
|
<description>JUnit 4 test detection</description>
|
||||||
<rule-property name="junit4TestPattern">[a-z][A-Za-z]*Test</rule-property>
|
<rule-property name="junit4TestPattern">[a-z][A-Za-z]*Test</rule-property>
|
||||||
<expected-problems>2</expected-problems>
|
<expected-problems>2</expected-problems>
|
||||||
<expected-linenumbers>12,16</expected-linenumbers>
|
<expected-linenumbers>8,12</expected-linenumbers>
|
||||||
<expected-messages>
|
<expected-messages>
|
||||||
<message>The JUnit 4 test method name 'testGetBestTeam' doesn't match '[a-z][A-Za-z]*Test'</message>
|
<message>The JUnit 4 test method name 'testGetBestTeam' doesn't match '[a-z][A-Za-z]*Test'</message>
|
||||||
<message>The JUnit 4 test method name 'getBestTeam' doesn't match '[a-z][A-Za-z]*Test'</message>
|
<message>The JUnit 4 test method name 'getBestTeam' doesn't match '[a-z][A-Za-z]*Test'</message>
|
||||||
</expected-messages>
|
</expected-messages>
|
||||||
<code><![CDATA[
|
<code><![CDATA[
|
||||||
import junit.framework.Assert;
|
import junit.framework.TestCase;
|
||||||
import junit.framework.TestCase;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.junit.Test;
|
public class TournamentTest extends TestCase {
|
||||||
|
Tournament tournament;
|
||||||
|
|
||||||
|
@Test // note that this is also a junit 3 test, but the junit4 rule applies
|
||||||
|
public void testGetBestTeam() {
|
||||||
|
}
|
||||||
|
|
||||||
public class TournamentTest extends TestCase {
|
@Test // this is just a junit 4 test
|
||||||
Tournament tournament;
|
public void getBestTeam() {
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is ok
|
||||||
|
@Test
|
||||||
|
public void getBestTeamTest() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></code>
|
||||||
|
</test-code>
|
||||||
|
|
||||||
@Test // note that this is also a junit 3 test, but the junit4 rule applies
|
<test-code useAuxClasspath="false">
|
||||||
public void testGetBestTeam() {
|
<description>JUnit 4 test detection without proper auxclasspath</description>
|
||||||
}
|
<expected-problems>1</expected-problems>
|
||||||
|
<expected-linenumbers>8</expected-linenumbers>
|
||||||
|
<expected-messages>
|
||||||
|
<message>The JUnit 4 test method name 'get_best_team' doesn't match '[a-z][a-zA-Z0-9]*'</message>
|
||||||
|
</expected-messages>
|
||||||
|
<code><![CDATA[
|
||||||
|
import org.junit.Test; // note: test case uses "useAuxClasspath=false"!!
|
||||||
|
|
||||||
@Test // this is just a junit 4 test
|
public class TournamentTest {
|
||||||
public void getBestTeam() {
|
Tournament tournament;
|
||||||
}
|
|
||||||
|
|
||||||
// this is ok
|
// wrong test name pattern
|
||||||
@Test
|
@Test
|
||||||
public void getBestTeamTest() {
|
public void get_best_team() {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
]]>
|
// this is ok
|
||||||
</code>
|
@Test
|
||||||
|
public void getBestTeam() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]></code>
|
||||||
</test-code>
|
</test-code>
|
||||||
|
|
||||||
<test-code>
|
<test-code>
|
||||||
|
@ -274,6 +274,20 @@ public abstract class RuleTst {
|
|||||||
if (isUseAuxClasspath) {
|
if (isUseAuxClasspath) {
|
||||||
// configure the "auxclasspath" option for unit testing
|
// configure the "auxclasspath" option for unit testing
|
||||||
p.getConfiguration().prependClasspath(".");
|
p.getConfiguration().prependClasspath(".");
|
||||||
|
} else {
|
||||||
|
// simple class loader, that doesn't delegate to parent.
|
||||||
|
// this allows us in the tests to simulate PMD run without
|
||||||
|
// auxclasspath, not even the classes from the test dependencies
|
||||||
|
// will be found.
|
||||||
|
p.getConfiguration().setClassLoader(new ClassLoader() {
|
||||||
|
@Override
|
||||||
|
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
|
||||||
|
if (name.startsWith("java.") || name.startsWith("javax.")) {
|
||||||
|
return super.loadClass(name, resolve);
|
||||||
|
}
|
||||||
|
throw new ClassNotFoundException(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
RuleContext ctx = new RuleContext();
|
RuleContext ctx = new RuleContext();
|
||||||
ctx.setReport(report);
|
ctx.setReport(report);
|
||||||
|
Reference in New Issue
Block a user