[java] Properly resolve array types
- Honor dimensions of arrays - Resolve types for allocations as well as declarations of arrays
This commit is contained in:
@ -2024,9 +2024,9 @@ void ArrayDimsAndInits() :
|
||||
{
|
||||
|
||||
LOOKAHEAD(2)
|
||||
( LOOKAHEAD(2) (Annotation() {checkForBadTypeAnnotations();})* "[" Expression() "]" )+ ( LOOKAHEAD(2) "[" "]" )*
|
||||
( LOOKAHEAD(2) (Annotation() {checkForBadTypeAnnotations();})* "[" Expression() "]" {jjtThis.bumpArrayDepth();})+ ( LOOKAHEAD(2) "[" "]" {jjtThis.bumpArrayDepth();} )*
|
||||
|
|
||||
( "[" "]" )+ ArrayInitializer()
|
||||
( "[" "]" {jjtThis.bumpArrayDepth();})+ ArrayInitializer()
|
||||
}
|
||||
|
||||
|
||||
|
@ -6,6 +6,8 @@
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
public class ASTArrayDimsAndInits extends AbstractJavaNode {
|
||||
private int dimensions;
|
||||
|
||||
public ASTArrayDimsAndInits(int id) {
|
||||
super(id);
|
||||
}
|
||||
@ -20,4 +22,12 @@ public class ASTArrayDimsAndInits extends AbstractJavaNode {
|
||||
public Object jjtAccept(JavaParserVisitor visitor, Object data) {
|
||||
return visitor.visit(this, data);
|
||||
}
|
||||
|
||||
public void bumpArrayDepth() {
|
||||
dimensions++;
|
||||
}
|
||||
|
||||
public int getDimensions() {
|
||||
return dimensions;
|
||||
}
|
||||
}
|
||||
|
@ -61,4 +61,12 @@ public class ASTClassOrInterfaceType extends AbstractJavaTypeNode {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public int getArrayDepth() {
|
||||
Node p = jjtGetParent();
|
||||
if (p instanceof ASTReferenceType) {
|
||||
return ((ASTReferenceType) p).getArrayDepth();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,16 @@ public class VariableNameDeclaration extends AbstractNameDeclaration implements
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public int getArrayDepth() {
|
||||
ASTVariableDeclaratorId astVariableDeclaratorId = (ASTVariableDeclaratorId) node;
|
||||
ASTType typeNode = astVariableDeclaratorId.getTypeNode();
|
||||
if (typeNode != null) {
|
||||
return ((Dimensionable) typeNode.jjtGetParent()).getArrayDepth();
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isVarargs() {
|
||||
ASTVariableDeclaratorId astVariableDeclaratorId = (ASTVariableDeclaratorId) node;
|
||||
|
@ -268,7 +268,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
}
|
||||
}
|
||||
|
||||
populateType(node, typeName, node.isArray());
|
||||
populateType(node, typeName, node.getArrayDepth());
|
||||
|
||||
ASTTypeArguments typeArguments = node.getFirstChildOfType(ASTTypeArguments.class);
|
||||
|
||||
@ -546,14 +546,12 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
|
||||
if (prefix instanceof ASTPrimaryPrefix
|
||||
&& prefix.jjtGetParent().jjtGetNumChildren() >= 2) {
|
||||
ASTArguments args = prefix.jjtGetParent().jjtGetChild(1).getFirstChildOfType(ASTArguments.class);
|
||||
return args;
|
||||
return prefix.jjtGetParent().jjtGetChild(1).getFirstChildOfType(ASTArguments.class);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Searches a JavaTypeDefinition and it's superclasses until a field with name {@code fieldImage} that
|
||||
* is visible from the {@code accessingClass} class. Once it's found, it's possibly generic type is
|
||||
@ -684,7 +682,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
}
|
||||
String name = node.getNameDeclaration().getTypeImage();
|
||||
if (name != null) {
|
||||
populateType(node, name, node.getNameDeclaration().isArray());
|
||||
populateType(node, name, node.getNameDeclaration().getArrayDepth());
|
||||
}
|
||||
return super.visit(node, data);
|
||||
}
|
||||
@ -1137,8 +1135,7 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
public Object visit(ASTAllocationExpression node, Object data) {
|
||||
super.visit(node, data);
|
||||
|
||||
if (node.jjtGetNumChildren() >= 2 && node.jjtGetChild(1) instanceof ASTArrayDimsAndInits
|
||||
|| node.jjtGetNumChildren() >= 3 && node.jjtGetChild(2) instanceof ASTArrayDimsAndInits) {
|
||||
if (node.hasDescendantOfType(ASTArrayDimsAndInits.class)) {
|
||||
//
|
||||
// Classes for Array types cannot be found directly using
|
||||
// reflection.
|
||||
@ -1147,28 +1144,11 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
// dimensionality, and then ask for the type from the instance. OMFG
|
||||
// that's ugly.
|
||||
//
|
||||
|
||||
// TODO Need to create utility method to allow array type creation
|
||||
// which will use
|
||||
// caching to avoid repeated object creation.
|
||||
// TODO Modify Parser to tell us array dimensions count.
|
||||
// TODO Parser seems to do some work to handle arrays in certain
|
||||
// case already.
|
||||
// Examine those to figure out what's going on, make sure _all_
|
||||
// array scenarios
|
||||
// are ultimately covered. Appears to use a Dimensionable interface
|
||||
// to handle
|
||||
// only a part of the APIs (not bump), but is implemented several
|
||||
// times, so
|
||||
// look at refactoring to eliminate duplication. Dimensionable is
|
||||
// also used
|
||||
// on AccessNodes for some scenarios, need to account for that.
|
||||
// Might be
|
||||
// missing some TypeNode candidates we can add to the AST and have
|
||||
// to deal
|
||||
// with here (e.g. FormalParameter)? Plus some existing usages may
|
||||
// be
|
||||
// incorrect.
|
||||
final Class<?> arrayType = ((TypeNode) node.jjtGetChild(0)).getType();
|
||||
if (arrayType != null) {
|
||||
final ASTArrayDimsAndInits dims = node.getFirstChildOfType(ASTArrayDimsAndInits.class);
|
||||
node.setType(Array.newInstance(arrayType, (int[]) Array.newInstance(int.class, dims.getDimensions())).getClass());
|
||||
}
|
||||
} else {
|
||||
rollupTypeUnary(node);
|
||||
}
|
||||
@ -1275,10 +1255,10 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
}
|
||||
|
||||
private void populateType(TypeNode node, String className) {
|
||||
populateType(node, className, false);
|
||||
populateType(node, className, 0);
|
||||
}
|
||||
|
||||
private void populateType(TypeNode node, String className, boolean isArray) {
|
||||
private void populateType(TypeNode node, String className, int arrayDimens) {
|
||||
|
||||
String qualifiedName = className;
|
||||
Class<?> myType = PRIMITIVE_TYPES.get(className);
|
||||
@ -1330,8 +1310,8 @@ public class ClassTypeResolver extends JavaParserVisitorAdapter {
|
||||
node.setTypeDefinition(parameter.getTypeDefinition());
|
||||
}
|
||||
} else {
|
||||
if (isArray) {
|
||||
myType = Array.newInstance(myType, 0).getClass();
|
||||
if (arrayDimens > 0) {
|
||||
myType = Array.newInstance(myType, (int[]) Array.newInstance(int.class, arrayDimens)).getClass();
|
||||
}
|
||||
node.setType(myType);
|
||||
}
|
||||
|
@ -622,7 +622,6 @@ public final class MethodTypeResolution {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isSubtypeable(JavaTypeDefinition parameter, ASTExpression argument) {
|
||||
if (argument.getTypeDefinition() == null) {
|
||||
LOG.log(Level.FINE, "No type information for node {0}", argument.toString());
|
||||
|
@ -71,6 +71,7 @@ import net.sourceforge.pmd.typeresolution.testdata.AnonymousClassFromInterface;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.AnonymousInnerClass;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.AnoymousExtendingObject;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.ArrayListFound;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.ArrayTypes;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.DefaultJavaLangImport;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.EnumWithAnonymousInnerClass;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.ExtraTopLevelClass;
|
||||
@ -748,6 +749,32 @@ public class ClassTypeResolverTest {
|
||||
assertEquals("All expressions not tested", index, expressions.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testArrayTypes() throws JaxenException {
|
||||
ASTCompilationUnit acu = parseAndTypeResolveForClass15(ArrayTypes.class);
|
||||
|
||||
List<AbstractJavaTypeNode> expressions = convertList(
|
||||
acu.findChildNodesWithXPath("//VariableDeclarator"),
|
||||
AbstractJavaTypeNode.class);
|
||||
|
||||
int index = 0;
|
||||
|
||||
// int[] a = new int[1];
|
||||
AbstractJavaTypeNode typeNode = expressions.get(index++);
|
||||
assertEquals(int[].class, typeNode.getType());
|
||||
for (AbstractJavaTypeNode n : typeNode.findDescendantsOfType(AbstractJavaTypeNode.class)) {
|
||||
assertEquals(int[].class, n.getType());
|
||||
}
|
||||
|
||||
// Object[][] b = new Object[1][0];
|
||||
assertEquals(Object[][].class, expressions.get(index++).getType());
|
||||
|
||||
// ArrayTypes[][][] c = new ArrayTypes[][][] { new ArrayTypes[1][2] };
|
||||
assertEquals(ArrayTypes[][][].class, expressions.get(index++).getType());
|
||||
|
||||
// Make sure we got them all
|
||||
assertEquals("All expressions not tested", index, expressions.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFieldAccess() throws JaxenException {
|
||||
|
15
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/ArrayTypes.java
vendored
Normal file
15
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/ArrayTypes.java
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.typeresolution.testdata;
|
||||
|
||||
public class ArrayTypes {
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void test() {
|
||||
int[] a = new int[1];
|
||||
Object[][] b = new Object[1][0];
|
||||
ArrayTypes[][][] c = new ArrayTypes[][][] { new ArrayTypes[1][2] };
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user