Java, typeres: add incorporation and tests
This commit is contained in:
@ -129,7 +129,7 @@ public abstract class BoundOrConstraint {
|
||||
return rightTypeVariable;
|
||||
}
|
||||
|
||||
public InferenceRuleType getRuleType() {
|
||||
public InferenceRuleType ruleType() {
|
||||
return ruleType;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,177 @@
|
||||
/**
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.typeresolution.typeinference;
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.EQUALITY;
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.SUBTYPE;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
public final class TypeInferenceResolver {
|
||||
|
||||
private TypeInferenceResolver() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* https://docs.oracle.com/javase/specs/jls/se8/html/jls-18.html#jls-18.3
|
||||
*/
|
||||
public static List<Constraint> incorporateBounds(List<Bound> currentBounds, List<Bound> newBounds) {
|
||||
// (In this section, S and T are inference variables or types, and U is a proper type. For conciseness, a bound
|
||||
// of the form α = T may also match a bound of the form T = α.)
|
||||
|
||||
List<Constraint> newConstraints = new ArrayList<>();
|
||||
|
||||
for (Bound first : currentBounds) {
|
||||
for (Bound second : newBounds) {
|
||||
Sides sides = getUnequalSides(first, second);
|
||||
if (sides == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (first.ruleType() == EQUALITY && second.ruleType() == EQUALITY) {
|
||||
// α = S and α = T imply ‹S = T›
|
||||
newConstraints.add(copyConstraint(first, second, getUnequalSides(first, second), EQUALITY));
|
||||
} else if (first.ruleType() == EQUALITY && second.ruleType() == SUBTYPE) {
|
||||
if (sides.second == Side.RIGHT) {
|
||||
// α = S and α <: T imply ‹S <: T›
|
||||
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
|
||||
} else {
|
||||
// α = S and T <: α imply ‹T <: S›
|
||||
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
|
||||
}
|
||||
|
||||
} else if (first.ruleType() == SUBTYPE && second.ruleType() == EQUALITY) {
|
||||
if (sides.first == Side.RIGHT) {
|
||||
// α <: T and α = S imply ‹S <: T›
|
||||
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
|
||||
} else {
|
||||
// T <: α and α = S imply ‹T <: S›
|
||||
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
|
||||
}
|
||||
|
||||
} else if (first.ruleType() == SUBTYPE && second.ruleType() == SUBTYPE) {
|
||||
if (sides.first == Side.LEFT && sides.second == Side.RIGHT) {
|
||||
// S <: α and α <: T imply ‹S <: T›
|
||||
newConstraints.add(copyConstraint(first, second, sides, SUBTYPE));
|
||||
} else if (sides.first == Side.RIGHT && sides.second == Side.LEFT) {
|
||||
// α <: T and S <: α imply ‹S <: T›
|
||||
newConstraints.add(copyConstraint(second, first, sides.copySwap(), SUBTYPE));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// α = U and S = T imply ‹S[α:=U] = T[α:=U]› TODO
|
||||
|
||||
// α = U and S <: T imply ‹S[α:=U] <: T[α:=U]› TODO
|
||||
}
|
||||
}
|
||||
|
||||
return newConstraints;
|
||||
}
|
||||
|
||||
private enum Side {
|
||||
LEFT, RIGHT
|
||||
}
|
||||
|
||||
private static class Sides {
|
||||
/* default */ final Side first;
|
||||
/* default */ final Side second;
|
||||
|
||||
/* default */ Sides(Side first, Side second) {
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
|
||||
/* default */ Sides copySwap() {
|
||||
return new Sides(second, first);
|
||||
}
|
||||
}
|
||||
|
||||
private static Sides getUnequalSides(BoundOrConstraint first, BoundOrConstraint second) {
|
||||
if (first.leftVariable() != null) {
|
||||
if (first.leftVariable() == second.leftVariable()) {
|
||||
return new Sides(Side.RIGHT, Side.RIGHT);
|
||||
} else if (first.leftVariable() == second.rightVariable()) {
|
||||
return new Sides(Side.RIGHT, Side.LEFT);
|
||||
}
|
||||
} else if (first.rightVariable() != null) {
|
||||
if (first.rightVariable() == second.leftVariable()) {
|
||||
return new Sides(Side.LEFT, Side.RIGHT);
|
||||
} else if (first.rightVariable() == second.rightVariable()) {
|
||||
return new Sides(Side.LEFT, Side.LEFT);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Constraint copyConstraint(BoundOrConstraint first, BoundOrConstraint second, Sides sides,
|
||||
InferenceRuleType rule) {
|
||||
if (sides.first == Side.LEFT) {
|
||||
if (sides.second == Side.LEFT) {
|
||||
if (first.leftVariable() != null) {
|
||||
if (second.leftVariable() != null) {
|
||||
return new Constraint(first.leftVariable(), second.leftVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.leftVariable(), second.leftProper(), rule);
|
||||
}
|
||||
} else {
|
||||
if (second.leftVariable() != null) {
|
||||
return new Constraint(first.leftProper(), second.leftVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.leftProper(), second.leftProper(), rule);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (first.leftVariable() != null) {
|
||||
if (second.rightVariable() != null) {
|
||||
return new Constraint(first.leftVariable(), second.rightVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.leftVariable(), second.rightProper(), rule);
|
||||
}
|
||||
} else {
|
||||
if (second.rightVariable() != null) {
|
||||
return new Constraint(first.leftProper(), second.rightVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.leftProper(), second.rightProper(), rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (sides.second == Side.LEFT) {
|
||||
if (first.rightVariable() != null) {
|
||||
if (second.leftVariable() != null) {
|
||||
return new Constraint(first.rightVariable(), second.leftVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.rightVariable(), second.leftProper(), rule);
|
||||
}
|
||||
} else {
|
||||
if (second.leftVariable() != null) {
|
||||
return new Constraint(first.rightProper(), second.leftVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.rightProper(), second.leftProper(), rule);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (first.rightVariable() != null) {
|
||||
if (second.rightVariable() != null) {
|
||||
return new Constraint(first.rightVariable(), second.rightVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.rightVariable(), second.rightProper(), rule);
|
||||
}
|
||||
} else {
|
||||
if (second.rightVariable() != null) {
|
||||
return new Constraint(first.rightProper(), second.rightVariable(), rule);
|
||||
} else {
|
||||
return new Constraint(first.rightProper(), second.rightProper(), rule);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -4,7 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.typeresolution;
|
||||
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.CONTAINS;
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.EQUALITY;
|
||||
import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType.LOOSE_INVOCATION;
|
||||
@ -12,17 +11,18 @@ import static net.sourceforge.pmd.lang.java.typeresolution.typeinference.Inferen
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typedefinition.JavaTypeDefinition;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Bound;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.BoundOrConstraint;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Constraint;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.InferenceRuleType;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.TypeInferenceResolver;
|
||||
import net.sourceforge.pmd.lang.java.typeresolution.typeinference.Variable;
|
||||
|
||||
public class TypeInferenceTest {
|
||||
@ -197,13 +197,106 @@ public class TypeInferenceTest {
|
||||
// If T is a wildcard of the form ? super T': TODO
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIncorporation() {
|
||||
List<Constraint> result;
|
||||
List<Bound> current = new ArrayList<>();
|
||||
List<Bound> newBounds = new ArrayList<>();
|
||||
|
||||
JavaTypeDefinition s = JavaTypeDefinition.forClass(int.class);
|
||||
JavaTypeDefinition t = JavaTypeDefinition.forClass(double.class);
|
||||
Variable alpha = a;
|
||||
|
||||
// ### Original rule 1. : α = S and α = T imply ‹S = T›
|
||||
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(alpha, t, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
|
||||
|
||||
// α = S and T = α imply ‹S = T›
|
||||
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(t, alpha, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
|
||||
|
||||
// S = α and α = T imply ‹S = T›
|
||||
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
|
||||
|
||||
// S = α and T = α imply ‹S = T›
|
||||
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(t, alpha, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, EQUALITY, Constraint.class);
|
||||
|
||||
|
||||
// ### Original rule 2. : α = S and α <: T imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(alpha, t, SUBTYPE));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
// S = α and α <: T imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, SUBTYPE));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
// α <: T and α = S imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(alpha, s, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
// α <: T and S = α imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(s, alpha, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
|
||||
// ### Original rule 3. : α = S and T <: α imply ‹T <: S›
|
||||
result = incorporationResult(new Bound(alpha, s, EQUALITY), new Bound(t, alpha, SUBTYPE));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
|
||||
|
||||
// S = α and T <: α imply ‹T <: S›
|
||||
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(t, alpha, SUBTYPE));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
|
||||
|
||||
// T <: α and α = S imply ‹T <: S›
|
||||
result = incorporationResult(new Bound(t, alpha, SUBTYPE), new Bound(alpha, s, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
|
||||
|
||||
// T <: α and S = α imply ‹T <: S›
|
||||
result = incorporationResult(new Bound(t, alpha, SUBTYPE), new Bound(s, alpha, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), t, s, SUBTYPE, Constraint.class);
|
||||
|
||||
|
||||
// ### Original rule 4. : S <: α and α <: T imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(s, alpha, EQUALITY), new Bound(alpha, t, SUBTYPE));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
// α <: T and S <: α imply ‹S <: T›
|
||||
result = incorporationResult(new Bound(alpha, t, SUBTYPE), new Bound(s, alpha, EQUALITY));
|
||||
assertEquals(result.size(), 1);
|
||||
testBoundOrConstraint(result.get(0), s, t, SUBTYPE, Constraint.class);
|
||||
|
||||
}
|
||||
|
||||
private List<Constraint> incorporationResult(Bound firstBound, Bound secondBound) {
|
||||
List<Bound> current = new ArrayList<>();
|
||||
List<Bound> newBounds = new ArrayList<>();
|
||||
current.add(firstBound);
|
||||
newBounds.add(secondBound);
|
||||
return TypeInferenceResolver.incorporateBounds(current, newBounds);
|
||||
}
|
||||
|
||||
|
||||
private void testBoundOrConstraint(BoundOrConstraint val, JavaTypeDefinition left, JavaTypeDefinition right,
|
||||
InferenceRuleType rule, Class<? extends BoundOrConstraint> type) {
|
||||
assertTrue(val.getClass() == type);
|
||||
assertEquals(val.leftProper(), left);
|
||||
assertEquals(val.rightProper(), right);
|
||||
assertEquals(val.getRuleType(), rule);
|
||||
assertEquals(val.ruleType(), rule);
|
||||
}
|
||||
|
||||
|
||||
@ -212,7 +305,7 @@ public class TypeInferenceTest {
|
||||
assertTrue(val.getClass() == type);
|
||||
assertEquals(val.leftProper(), left);
|
||||
assertEquals(val.rightVariable(), right);
|
||||
assertEquals(val.getRuleType(), rule);
|
||||
assertEquals(val.ruleType(), rule);
|
||||
}
|
||||
|
||||
private void testBoundOrConstraint(BoundOrConstraint val, Variable left, JavaTypeDefinition right,
|
||||
@ -220,7 +313,7 @@ public class TypeInferenceTest {
|
||||
assertTrue(val.getClass() == type);
|
||||
assertEquals(val.leftVariable(), left);
|
||||
assertEquals(val.rightProper(), right);
|
||||
assertEquals(val.getRuleType(), rule);
|
||||
assertEquals(val.ruleType(), rule);
|
||||
}
|
||||
|
||||
private void testBoundOrConstraint(BoundOrConstraint val, Variable left, Variable right,
|
||||
@ -228,6 +321,6 @@ public class TypeInferenceTest {
|
||||
assertTrue(val.getClass() == type);
|
||||
assertEquals(val.leftVariable(), left);
|
||||
assertEquals(val.rightVariable(), right);
|
||||
assertEquals(val.getRuleType(), rule);
|
||||
assertEquals(val.ruleType(), rule);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user