forked from phoedos/pmd
Java, typeres: basic type variable resolution in place
This commit is contained in:
pmd-java/src
main/java/net/sourceforge/pmd/lang/java/typeresolution
test/java/net/sourceforge/pmd/typeresolution
@ -500,7 +500,21 @@ public final class MethodTypeResolution {
|
||||
|
||||
// this covers arrays, simple class/interface cases
|
||||
if (parameter.getType().isAssignableFrom(argument.getType())) {
|
||||
return true;
|
||||
if (!parameter.isGeneric() || parameter.isRawType() || argument.isRawType()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// parameter is a non-raw generic type
|
||||
// argument is a non-generic or a non-raw generic type
|
||||
|
||||
// example result: List<String>.getAsSuper(Collection) becomes Collection<String>
|
||||
JavaTypeDefinition argSuper = argument.getAsSuper(parameter.getType());
|
||||
// argSuper can't be null because isAssignableFrom check above returned true
|
||||
|
||||
// right now we only check if generic arguments are the same
|
||||
// TODO: add support for wildcard types
|
||||
// (future note: can't call subtype as it is recursively, infinite types)
|
||||
return parameter.equals(argSuper);
|
||||
}
|
||||
|
||||
int indexOfParameter = PRIMITIVE_SUBTYPE_ORDER.indexOf(parameter.getType());
|
||||
@ -532,12 +546,12 @@ public final class MethodTypeResolution {
|
||||
public static List<JavaTypeDefinition> getMethodExplicitTypeArugments(Node node) {
|
||||
ASTMemberSelector memberSelector = node.getFirstChildOfType(ASTMemberSelector.class);
|
||||
if (memberSelector == null) {
|
||||
return Collections.emptyList(); // empty list
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
ASTTypeArguments typeArguments = memberSelector.getFirstChildOfType(ASTTypeArguments.class);
|
||||
if (typeArguments == null) {
|
||||
return Collections.emptyList(); // empty list
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<JavaTypeDefinition> result = new ArrayList<>();
|
||||
|
@ -10,7 +10,6 @@ import java.lang.reflect.Type;
|
||||
import java.lang.reflect.TypeVariable;
|
||||
import java.lang.reflect.WildcardType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@ -329,7 +328,7 @@ public class JavaTypeDefinition implements TypeDefinition {
|
||||
destinationSet.add(clazz);
|
||||
getErasedSuperTypeSet(clazz.getSuperclass(), destinationSet);
|
||||
|
||||
for(Class<?> interfaceType : clazz.getInterfaces()) {
|
||||
for (Class<?> interfaceType : clazz.getInterfaces()) {
|
||||
getErasedSuperTypeSet(interfaceType, destinationSet);
|
||||
}
|
||||
}
|
||||
@ -345,8 +344,12 @@ public class JavaTypeDefinition implements TypeDefinition {
|
||||
}
|
||||
|
||||
public JavaTypeDefinition getAsSuper(Class<?> superClazz) {
|
||||
for(JavaTypeDefinition superTypeDef : getSuperTypeSet()) {
|
||||
if(superTypeDef.clazz == superClazz) {
|
||||
if (clazz == superClazz) { // optimize for same class calls
|
||||
return this;
|
||||
}
|
||||
|
||||
for (JavaTypeDefinition superTypeDef : getSuperTypeSet()) {
|
||||
if (superTypeDef.clazz == superClazz) {
|
||||
return superTypeDef;
|
||||
}
|
||||
}
|
||||
|
@ -137,11 +137,11 @@ public abstract class BoundOrConstraint {
|
||||
public abstract List<BoundOrConstraint> reduce();
|
||||
|
||||
public void addVariablesToSet(Set<Variable> variables) {
|
||||
if(leftTypeVariable != null) {
|
||||
if (leftTypeVariable != null) {
|
||||
variables.add(leftTypeVariable);
|
||||
}
|
||||
|
||||
if(rightTypeVariable != null) {
|
||||
if (rightTypeVariable != null) {
|
||||
variables.add(rightTypeVariable);
|
||||
}
|
||||
}
|
||||
|
@ -4,9 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.typeresolution.typeinference;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.MethodTypeResolution;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.EQUALITY;
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.SUBTYPE;
|
||||
|
||||
@ -20,6 +17,9 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.MethodTypeResolution;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
|
||||
|
||||
|
||||
public final class TypeInferenceResolver {
|
||||
|
||||
@ -39,11 +39,14 @@ public final class TypeInferenceResolver {
|
||||
}
|
||||
}
|
||||
|
||||
// step 1 - EC
|
||||
Set<Class<?>> erasedCandidateSet = getErasedCandidateSet(types);
|
||||
// step 2 - MEC
|
||||
Set<Class<?>> minimalSet = getMinimalErasedCandidateSet(erasedCandidateSet);
|
||||
|
||||
List<JavaTypeDefinition> candidates = new ArrayList<>();
|
||||
|
||||
// for each element G n MEC
|
||||
for (Class<?> erasedSupertype : minimalSet) {
|
||||
JavaTypeDefinition lci = types.get(0).getAsSuper(erasedSupertype);
|
||||
|
||||
@ -65,17 +68,21 @@ public final class TypeInferenceResolver {
|
||||
JavaTypeDefinition result = candidates.get(0);
|
||||
|
||||
for (JavaTypeDefinition candidate : candidates) {
|
||||
if (containsType(candidate, result)) {
|
||||
if (MethodTypeResolution.isSubtypeable(candidate, result)) {
|
||||
result = candidate;
|
||||
} else if (!containsType(result, candidate)) { // TODO: add support for compound types
|
||||
} else if (!MethodTypeResolution.isSubtypeable(result, candidate)) {
|
||||
// TODO: add support for compound types
|
||||
throw new ResolutionFailed();
|
||||
}
|
||||
} // else: result contains candidate, nothing else to do
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static JavaTypeDefinition intersect(JavaTypeDefinition first, JavaTypeDefinition second) {
|
||||
/**
|
||||
* @return the intersection of the two types
|
||||
*/
|
||||
public static JavaTypeDefinition intersect(JavaTypeDefinition first, JavaTypeDefinition second) {
|
||||
if (first.equals(second)) { // two types equal
|
||||
return first;
|
||||
} else if (first.getType() == second.getType()) {
|
||||
@ -90,38 +97,30 @@ public final class TypeInferenceResolver {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true, if parameter contains argument
|
||||
* Merge two types of the same class to something both can be assigned to and is most specific.
|
||||
*/
|
||||
public static boolean containsType(JavaTypeDefinition parameter, JavaTypeDefinition argument) {
|
||||
if (!MethodTypeResolution.isSubtypeable(parameter, argument)) {
|
||||
return false; // class can't be converted even with unchecked conversion
|
||||
}
|
||||
|
||||
// TODO: wildcards, checking generic arguments properly
|
||||
if (parameter.equals(argument)) {
|
||||
// we don't care about List<String> is assigable to Collection<String> or Collection<? extends Object>
|
||||
return true; // we don't yet care about wildcards like, List<String> is assignable to List<? extends Object>
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static JavaTypeDefinition merge(JavaTypeDefinition first, JavaTypeDefinition second) {
|
||||
if (first.getType() != second.getType()) {
|
||||
throw new IllegalStateException("Must be called with typedefinitions of the same class");
|
||||
}
|
||||
|
||||
if (!first.isGeneric()) {
|
||||
return first;
|
||||
}
|
||||
|
||||
|
||||
JavaTypeDefinition[] mergedGeneric = new JavaTypeDefinition[first.getTypeParameterCount()];
|
||||
|
||||
for (int i = 0; i < first.getTypeParameterCount(); ++i) {
|
||||
if (containsType(first.getGenericType(i), second.getGenericType(i))) {
|
||||
if (MethodTypeResolution.isSubtypeable(first.getGenericType(i), second.getGenericType(i))) {
|
||||
mergedGeneric[i] = first.getGenericType(i);
|
||||
} else if (containsType(second.getGenericType(i), first.getGenericType(i))) {
|
||||
} else if (MethodTypeResolution.isSubtypeable(second.getGenericType(i), first.getGenericType(i))) {
|
||||
mergedGeneric[i] = second.getGenericType(i);
|
||||
} else {
|
||||
return JavaTypeDefinition.forClass(Object.class);
|
||||
// TODO: Generic types of the same class can be merged like so:
|
||||
// List<Integer> List<Double> -> List<? extends Number> but we don't have wildcards yet
|
||||
// List<Integer> List<Double> -> List<? extends Number>
|
||||
// but we don't have wildcards yet
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,15 +174,47 @@ public final class TypeInferenceResolver {
|
||||
|
||||
// Note: since the Combinations class enumerates the power set from least numerous to most numerous sets
|
||||
// the above requirement is satisfied
|
||||
|
||||
List<Variable> variablesToResolve = null;
|
||||
|
||||
for (List<Variable> variableSet : new Combinations(uninstantiatedVariables)) {
|
||||
if (isProperSubsetOfVariables(variableSet, instantiations, variableDependencies, bounds)) {
|
||||
// TODO: resolve variables
|
||||
variablesToResolve = variableSet;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (variablesToResolve == null) {
|
||||
throw new ResolutionFailed();
|
||||
}
|
||||
|
||||
// if there are least upper bounds
|
||||
for (Variable var : variablesToResolve) {
|
||||
List<JavaTypeDefinition> lowerBounds = getLowerBoundsOf(var, bounds);
|
||||
// TODO: should call incorporation
|
||||
instantiations.put(var, lub(lowerBounds));
|
||||
}
|
||||
|
||||
uninstantiatedVariables.removeAll(variablesToResolve);
|
||||
}
|
||||
|
||||
return instantiations;
|
||||
}
|
||||
|
||||
public static List<JavaTypeDefinition> getLowerBoundsOf(Variable var, List<Bound> bounds) {
|
||||
List<JavaTypeDefinition> result = new ArrayList<>();
|
||||
for (Bound bound : bounds) {
|
||||
if (bound.ruleType() == SUBTYPE && bound.rightVariable() == var) {
|
||||
// TODO: add support for variables depending on other variables
|
||||
if (bound.isLeftVariable()) {
|
||||
throw new ResolutionFailed();
|
||||
}
|
||||
|
||||
result.add(bound.leftProper());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return instantiations;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,10 +235,11 @@ public final class TypeInferenceResolver {
|
||||
Map<Variable, Set<Variable>> dependencies,
|
||||
List<Bound> bounds) {
|
||||
|
||||
// search the bounds for an
|
||||
|
||||
for (Variable unresolvedVariable : variables) {
|
||||
for (Variable dependency : dependencies.get(unresolvedVariable)) {
|
||||
if (!instantiations.containsKey(dependency)
|
||||
&& unresolvedVariable != dependency
|
||||
&& !boundsHaveAnEqualityBetween(variables, dependency, bounds)) {
|
||||
return false;
|
||||
}
|
||||
@ -247,7 +279,7 @@ public final class TypeInferenceResolver {
|
||||
private List<Variable> resultList = new ArrayList<>();
|
||||
private List<Variable> unmodifyableViewOfResult = Collections.unmodifiableList(resultList);
|
||||
|
||||
public Combinations(List<Variable> permuteThis) {
|
||||
Combinations(List<Variable> permuteThis) {
|
||||
this.permuteThis = permuteThis;
|
||||
this.n = permuteThis.size();
|
||||
this.k = 0;
|
||||
|
@ -15,13 +15,11 @@ import java.io.InputStream;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.JavaTypeDefinitionEquals;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.jaxen.JaxenException;
|
||||
|
||||
@ -92,6 +90,7 @@ import net.sourceforge.pmd.typeresolution.testdata.SuperExpression;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.ThisExpression;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.Converter;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.GenericClass;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.JavaTypeDefinitionEquals;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.StaticMembers;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA2;
|
||||
|
@ -17,7 +17,6 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.tools.ant.taskdefs.Java;
|
||||
import org.junit.Test;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
|
||||
@ -28,6 +27,11 @@ import net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleT
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.TypeInferenceResolver;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Variable;
|
||||
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassA2;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassAOther;
|
||||
import net.sourceforge.pmd.typeresolution.testdata.dummytypes.SuperClassAOther2;
|
||||
|
||||
public class TypeInferenceTest {
|
||||
private JavaTypeDefinition number = JavaTypeDefinition.forClass(Number.class);
|
||||
private JavaTypeDefinition integer = JavaTypeDefinition.forClass(Integer.class);
|
||||
@ -320,10 +324,21 @@ public class TypeInferenceTest {
|
||||
@Test
|
||||
public void testLeastUpperBound() {
|
||||
List<JavaTypeDefinition> lowerBounds = new ArrayList<>();
|
||||
lowerBounds.add(JavaTypeDefinition.forClass(String.class));
|
||||
lowerBounds.add(JavaTypeDefinition.forClass(.class));
|
||||
lowerBounds.add(JavaTypeDefinition.forClass(SuperClassA.class));
|
||||
lowerBounds.add(JavaTypeDefinition.forClass(SuperClassAOther.class));
|
||||
lowerBounds.add(JavaTypeDefinition.forClass(SuperClassAOther2.class));
|
||||
|
||||
JavaTypeDefinition result = TypeInferenceResolver.lub(lowerBounds);
|
||||
assertEquals(TypeInferenceResolver.lub(lowerBounds), JavaTypeDefinition.forClass(SuperClassA2.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testResolution() {
|
||||
List<Bound> bounds = new ArrayList<>();
|
||||
bounds.add(new Bound(JavaTypeDefinition.forClass(SuperClassA.class), alpha, SUBTYPE));
|
||||
bounds.add(new Bound(JavaTypeDefinition.forClass(SuperClassAOther.class), alpha, SUBTYPE));
|
||||
Map<Variable, JavaTypeDefinition> result = TypeInferenceResolver.resolveVariables(bounds);
|
||||
assertEquals(result.size(), 1);
|
||||
assertEquals(result.get(alpha), JavaTypeDefinition.forClass(SuperClassA2.class));
|
||||
}
|
||||
|
||||
private List<Constraint> incorporationResult(Bound firstBound, Bound secondBound) {
|
||||
|
@ -1,3 +1,8 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
package net.sourceforge.pmd.typeresolution.testdata.dummytypes;
|
||||
|
||||
import java.util.List;
|
||||
|
9
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/SuperClassAOther.java
vendored
Normal file
9
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/SuperClassAOther.java
vendored
Normal 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 SuperClassAOther extends SuperClassA2 {
|
||||
}
|
9
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/SuperClassAOther2.java
vendored
Normal file
9
pmd-java/src/test/java/net/sourceforge/pmd/typeresolution/testdata/dummytypes/SuperClassAOther2.java
vendored
Normal 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 SuperClassAOther2 extends SuperClassA2 {
|
||||
}
|
Reference in New Issue
Block a user