Merge branch 'pr-444'

This commit is contained in:
Juan Martín Sotuyo Dodero
2017-06-24 15:43:59 -03:00
25 changed files with 1206 additions and 157 deletions

View File

@ -47,4 +47,8 @@ public class ASTClassOrInterfaceType extends AbstractJavaTypeNode {
}
return false;
}
public boolean isAnonymousClass() {
return jjtGetParent().hasDescendantOfType(ASTClassOrInterfaceBody.class);
}
}

View File

@ -5,7 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
public class ASTTypeArgument extends AbstractJavaNode {
public class ASTTypeArgument extends AbstractJavaTypeNode {
public ASTTypeArgument(int id) {
super(id);
}

View File

@ -5,7 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
public class ASTTypeBound extends AbstractJavaNode {
public class ASTTypeBound extends AbstractJavaTypeNode {
public ASTTypeBound(int id) {
super(id);
}

View File

@ -5,7 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
public class ASTTypeParameter extends AbstractJavaNode {
public class ASTTypeParameter extends AbstractJavaTypeNode {
public ASTTypeParameter(int id) {
super(id);
}

View File

@ -4,9 +4,10 @@
package net.sourceforge.pmd.lang.java.ast;
public abstract class AbstractJavaAccessTypeNode extends AbstractJavaAccessNode implements TypeNode {
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
private Class<?> type;
public abstract class AbstractJavaAccessTypeNode extends AbstractJavaAccessNode implements TypeNode {
private JavaTypeDefinition typeDefinition;
public AbstractJavaAccessTypeNode(int i) {
super(i);
@ -18,11 +19,25 @@ public abstract class AbstractJavaAccessTypeNode extends AbstractJavaAccessNode
@Override
public Class<?> getType() {
return type;
if (typeDefinition != null) {
return typeDefinition.getType();
}
return null;
}
@Override
public void setType(Class<?> type) {
this.type = type;
typeDefinition = JavaTypeDefinition.build(type);
}
@Override
public JavaTypeDefinition getTypeDefinition() {
return typeDefinition;
}
@Override
public void setTypeDefinition(JavaTypeDefinition typeDefinition) {
this.typeDefinition = typeDefinition;
}
}

View File

@ -4,15 +4,16 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
/**
* An extension of the SimpleJavaNode which implements the TypeNode interface.
*
*
* @see AbstractJavaNode
* @see TypeNode
*/
public abstract class AbstractJavaTypeNode extends AbstractJavaNode implements TypeNode {
private Class<?> type;
private JavaTypeDefinition typeDefinition;
public AbstractJavaTypeNode(int i) {
super(i);
@ -24,11 +25,25 @@ public abstract class AbstractJavaTypeNode extends AbstractJavaNode implements T
@Override
public Class<?> getType() {
return type;
if (typeDefinition != null) {
return typeDefinition.getType();
}
return null;
}
@Override
public void setType(Class<?> type) {
this.type = type;
typeDefinition = JavaTypeDefinition.build(type);
}
@Override
public JavaTypeDefinition getTypeDefinition() {
return typeDefinition;
}
@Override
public void setTypeDefinition(JavaTypeDefinition typeDefinition) {
this.typeDefinition = typeDefinition;
}
}

View File

@ -5,6 +5,7 @@
package net.sourceforge.pmd.lang.java.ast;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
/**
* This interface allows a Java Class to be associated with a node.
@ -13,16 +14,31 @@ public interface TypeNode extends Node {
/**
* Get the Java Class associated with this node.
*
*
* @return The Java Class, may return <code>null</code>.
*/
Class<?> getType();
/**
* Get the TypeDefinition associated with this node. The Class object
* contained in the TypeDefinition will always be equal to that which
* is returned by <code>getType()</code>.
*
* @return The TypeDefinition, may return <code>null</code>
*/
JavaTypeDefinition getTypeDefinition();
/**
* Set the TypeDefinition associated with this node.
*
* @param type A TypeDefinition object
*/
void setTypeDefinition(JavaTypeDefinition type);
/**
* Set the Java Class associated with this node.
*
* @param type
* A Java Class
*
* @param type A Java Class
*/
void setType(Class<?> type);
}

View File

@ -0,0 +1,74 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.typeresolution.typedefinition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JavaTypeDefinition implements TypeDefinition {
private final Class<?> clazz;
private List<JavaTypeDefinition> genericArgs;
// contains TypeDefs where only the clazz field is used
private static Map<Class<?>, JavaTypeDefinition> onlyClassTypeDef = new HashMap<>();
public Class<?> getType() {
return clazz;
}
public List<JavaTypeDefinition> getGenericArgs() {
if (genericArgs == null) {
genericArgs = Collections.unmodifiableList(new ArrayList<JavaTypeDefinition>());
}
return genericArgs;
}
private JavaTypeDefinition(Class<?> clazz, List<JavaTypeDefinition> genericArgs) {
this.clazz = clazz;
if (genericArgs != null) {
this.genericArgs = Collections.unmodifiableList(genericArgs);
}
}
// builder part of the class
public static JavaTypeDefinition build(Class<?> clazz) {
if (onlyClassTypeDef.containsKey(clazz)) {
return onlyClassTypeDef.get(clazz);
}
JavaTypeDefinition typeDef = new JavaTypeDefinition(clazz, null);
onlyClassTypeDef.put(clazz, typeDef);
return typeDef;
}
/**
* @param genericArgs This package private method expects that the genericArgs list has not been leaked,
* meaning the other references have been discarded to ensure immutability.
*/
/* default */ static JavaTypeDefinition build(Class<?> clazz, List<JavaTypeDefinition> genericArgs) {
if (genericArgs == null) {
return build(clazz);
}
return new JavaTypeDefinition(clazz, genericArgs);
}
public static JavaTypeDefinitionBuilder builder(Class<?> clazz) {
return new JavaTypeDefinitionBuilder().setType(clazz);
}
public static JavaTypeDefinitionBuilder builder() {
return new JavaTypeDefinitionBuilder();
}
}

View File

@ -0,0 +1,39 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.typeresolution.typedefinition;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class JavaTypeDefinitionBuilder {
private Class<?> clazz = null;
private List<JavaTypeDefinition> genericArgs = new ArrayList<>();
/* default */ JavaTypeDefinitionBuilder() {}
public JavaTypeDefinitionBuilder addTypeArg(JavaTypeDefinition arg) {
genericArgs.add(arg);
return this;
}
public List<JavaTypeDefinition> getTypeArgs() {
return Collections.unmodifiableList(genericArgs);
}
public JavaTypeDefinitionBuilder getTypeArg(int index) {
genericArgs.get(index);
return this;
}
public JavaTypeDefinitionBuilder setType(Class<?> clazz) {
this.clazz = clazz;
return this;
}
public JavaTypeDefinition build() {
return JavaTypeDefinition.build(clazz, genericArgs);
}
}

View File

@ -0,0 +1,23 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.typeresolution.typedefinition;
import java.util.List;
public interface TypeDefinition {
/**
* Get the raw Class type of the definition.
*
* @return Raw Class type.
*/
Class<?> getType();
/**
* Get the list of type arguments for this TypeDefinition.
*
* @return An ordered and immutable list of type arguments.
*/
List<? extends TypeDefinition> getGenericArgs();
}

View File

@ -12,10 +12,13 @@ import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA;
/*
* Note: inherited fields of a nested class shadow outer scope variables
* Note: only if they are accessible!
*
* TODO: test static field access, array types, anonymous class (super type access)
*/
public class FieldAccess extends SuperClassA {
public int field;
public FieldAccess f;
public static FieldAccess staticF;
public void foo(FieldAccess param) {
FieldAccess local = null;

View File

@ -0,0 +1,32 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericSuperClassA;
public class FieldAccessGenericBounds extends GenericSuperClassA<Long> {
GenericClass<? super String, ?> superGeneric;
GenericClass<? extends Number, Object> upperBound;
public void astPrimaryNameCases() {
// test ?, ? super Something, ? extends Something
// Primary[Prefix[Name[superGeneric.first]]]
superGeneric.first = ""; // Object
superGeneric.second = null; // Object
inheritedSuperGeneric.first = ""; // Object
inheritedSuperGeneric.second = null; // Object
upperBound.first = null; // Number
inheritedUpperBound.first = null; // String
// test static imports
// Primary[Prefix[Name[instanceFields.generic.first]]]
//instanceFields.generic.first = "";
//staticGeneric.first = new Long(0);
}
}

View File

@ -0,0 +1,35 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass;
public class FieldAccessGenericParameter<T extends GenericClass<String, GenericClass<String, Integer>>,
S extends Double> {
T parameterGeneric;
S classGeneric;
<M extends Character> void foo() {
M localGeneric = null;
// access type dependant on class/method type arguments
// Primary[Prefix[Name[classGeneric]]]
classGeneric = null; // Double
localGeneric = null; // Character
// test type parameters extending generic types
// Primary[Prefix[Name[parameterGeneric.first]]]
parameterGeneric.second.second = new Integer(0);
}
<C extends Number> FieldAccessGenericParameter() {
C constructorGeneric = null;
// access type dependant on constructor type arugments
// Primary[Prefix[Name[localGeneric]]]
constructorGeneric = null; // Number
}
}

View File

@ -0,0 +1,37 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass2;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericSuperClassA;
public class FieldAccessGenericRaw<T extends GenericClass2> extends GenericSuperClassA<Long> {
GenericClass2 rawGeneric;
T parameterRawGeneric;
void foo() {
// test raw types
// Primary[Prefix[Name[rawGeneric.first]]]
rawGeneric.first = new Integer(0);
rawGeneric.second = new Integer(0);
rawGeneric.third = new Object();
rawGeneric.fourth.second = "";
rawGeneric.rawGeneric.second = new Integer(0);
// Primary[Prefix[Name[inheritedGeneric.first]]]
inheritedRawGeneric.first = new Integer(0);
inheritedRawGeneric.second = new Integer(0);
inheritedRawGeneric.third = new Object();
inheritedRawGeneric.fourth.second = "";
inheritedRawGeneric.rawGeneric.second = new Integer(0);
// Primary[Prefix[Name[parameterRawGeneric.first]]]
parameterRawGeneric.first = new Integer(0);
parameterRawGeneric.second = new Integer(0);
parameterRawGeneric.third = new Object();
parameterRawGeneric.fourth.second = "";
parameterRawGeneric.rawGeneric.second = new Integer(0);
}
}

View File

@ -0,0 +1,60 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericSuperClassA;
/*
* TODO: add anonymous class this (Allocation expression)
* TODO: add primitives, parameterized arrays
* TODO: diamond, type parmeter declarations can shadow Class declarations
*/
public class FieldAccessGenericSimple extends GenericSuperClassA<Long> {
GenericClass<String, Double> genericField;
GenericClass<String, GenericClass<Number, Double>> genericTypeArg;
FieldAccessGenericSimple fieldAcc;
void foo(GenericClass<Integer, Character> param) {
GenericClass<Float, Long> local = null;
// access a generic field through member field
// Primary[Prefix[Name[genericField.first]]]
genericField.first = "";
genericField.second = new Double(0);
// access a generic field whose type depends on a generic type argument
// Primary[Prefix[Name[genericTypeArg.second.second]]]
genericTypeArg.second.second = new Double(0);
// access a generic field through a local or a parameter
// Primary[Prefix[Name[param.first]]]
param.first = new Integer(0);
local.second = new Long(0);
// access a generic field whose type depends on indirect type arguments
// Primary[Prefix[Name[generic.generic.first]]]
param.generic.first = new Character('c');
local.generic.second = new Float(0);
genericField.generic.generic.generic.first = new Double(0);
// test inherited generic
// Primary[Prefix[Name[generic.first]]]
fieldA = new Long(0);
fieldB.generic.second = "";
// test inherited generic
// Primary[Prefix[Name[fieldAcc.fieldA]]]
fieldAcc.fieldA = new Long(0);
}
public class Nested extends GenericSuperClassA<Long> {
void foo() {
fieldA = new Long(0);
}
}
}

View File

@ -29,6 +29,7 @@ public class FieldAccessNested {
a = new SuperClassA();
net.sourceforge.pmd.typeresolution.testdata.FieldAccessNested.Nested.this.a = new SuperClassA();
FieldAccessNested.Nested.this.a = new SuperClassA();
}
}
}

View File

@ -0,0 +1,49 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass;
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericSuperClassA;
public class FieldAccessPrimaryGenericSimple extends GenericSuperClassA<Long> {
GenericClass<String, Double> genericField;
GenericClass<String, GenericClass<Number, Double>> genericTypeArg;
void foo(GenericClass<Integer, Character> param) {
GenericClass<Float, Long> local = null;
// access a generic field through member field
// Primary[Prefix[this], Suffix[genericField], Suffix[first]]
this.genericField.first = "";
(this).genericField.second = new Double(0);
// access a generic field whose type depends on a generic type argument
// Primary[Prefix[this], Suffix[genericTypeArg], Suffix[second], Suffix[second]]
this.genericTypeArg.second.second = new Double(0);
// access a generic field whose type depends on indirect type arguments
// Primary[Prefix[this], Suffix[genericField], Suffix[generic], Suffix[generic]...]
(this).genericField.generic.generic.generic.first = new Double(0);
// test inherited generic
// Primary[Primary[Prefix[(this)]], Suffix[fieldA]]
(this).fieldA = new Long(0);
this.fieldB.generic.second = "";
// test inherited generic
// Primary[Prefix[super], Suffix[fieldA]]
super.fieldA = new Long(0);
super.fieldB.generic.second = "";
}
class Nested<T extends GenericClass<String, Number>> {
T field;
void foo() {
// Primary[Prefix[this], Suffix[field], Suffix[first]]
this.field.first = "";
}
}
}

View File

@ -16,13 +16,8 @@ import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassB2;
*/
public class FieldAccessShadow {
Integer field;
String s2;
public void foo() {
String field;

View File

@ -5,6 +5,8 @@
package net.sourceforge.pmd.typeresolution.testdata.dummytypes;
public class GenericClass<T> {
public T a;
public class GenericClass<T, S> {
public T first;
public S second;
public GenericClass<S, T> generic;
}

View File

@ -0,0 +1,19 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata.dummytypes;
public class GenericClass2<A extends Integer, B extends A, C,
S extends String,
D extends GenericClass<A, S>,
//, E extends GenericClass<E, E>,
F extends GenericClass2> {
public A first;
public B second;
public C third;
public D fourth;
//public E fifth; // recursion
public F sixth; // recursion
public GenericClass2 rawGeneric;
}

View File

@ -0,0 +1,12 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata.dummytypes;
public class GenericSuperClassA<T> extends GenericSuperClassB<T, GenericClass<String, T>> {
public T fieldA;
public GenericClass2 inheritedRawGeneric;
public GenericClass<? super String, ?> inheritedSuperGeneric;
public GenericClass<? extends String, Object> inheritedUpperBound;
}

View File

@ -0,0 +1,9 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.typeresolution.testdata.dummytypes;
public class GenericSuperClassB<T, S> {
public S fieldB;
}

View File

@ -27,7 +27,7 @@ His progress so far has allowed to properly resolve, in addition to previously s
- References to `this` and `super`, even when qualified
- References to fields, even when chained (ie: `this.myObject.aField`), and properly handling inheritance / shadowing
Fields using generics are still Work in Progress, but we expect to fully support it soon enough.
Lambda parameter types where these are infered rather than explicit are still not supported. Expect future releases to do so.
#### Metrics Framework
@ -120,5 +120,6 @@ by a new CyclomaticComplexity rule based on the metrics framework. See also [iss
* [#436](https://github.com/pmd/pmd/pull/436): \[java] Metrics framework tests and various improvements
* [#440](https://github.com/pmd/pmd/pull/440): \[core] Created ruleset schema 3.0.0 (to use metrics)
* [#443](https://github.com/pmd/pmd/pull/443): \[java] Optimize typeresolution, by skipping package and import declarations in visit(ASTName)
* [#444](https://github.com/pmd/pmd/pull/444): \[java] [typeresolution]: add support for generic fields
* [#451](https://github.com/pmd/pmd/pull/451): \[java] Metrics framework: first metrics + first rule