From 23ace58e6c70a54c654d62f996fb0bf5723787da Mon Sep 17 00:00:00 2001
From: Pavel Vojtechovsky
Date: Sun, 12 Mar 2017 16:20:58 +0100
Subject: [PATCH] feature CtTypeParameter#isSubtypeOf
---
.../spoon/reflect/declaration/CtMethod.java | 9 +
.../reflect/declaration/CtMethodImpl.java | 96 +++++++++-
.../declaration/CtTypeParameterImpl.java | 169 +++++++++++++++++-
.../test/ctType/CtTypeParameterTest.java | 87 +++++++++
.../java/spoon/test/ctType/CtTypeTest.java | 79 +++++---
.../ctType/testclasses/ErasureModelA.java | 5 +-
.../java/spoon/test/ctType/testclasses/O.java | 21 +++
.../java/spoon/test/ctType/testclasses/P.java | 30 ++++
.../java/spoon/test/ctType/testclasses/Q.java | 38 ++++
.../java/spoon/test/ctType/testclasses/R.java | 26 +++
.../java/spoon/test/ctType/testclasses/X.java | 11 --
.../java/spoon/test/ctType/testclasses/Y.java | 5 +
12 files changed, 535 insertions(+), 41 deletions(-)
create mode 100644 src/test/java/spoon/test/ctType/testclasses/O.java
create mode 100644 src/test/java/spoon/test/ctType/testclasses/P.java
create mode 100644 src/test/java/spoon/test/ctType/testclasses/Q.java
create mode 100644 src/test/java/spoon/test/ctType/testclasses/R.java
create mode 100644 src/test/java/spoon/test/ctType/testclasses/Y.java
diff --git a/src/main/java/spoon/reflect/declaration/CtMethod.java b/src/main/java/spoon/reflect/declaration/CtMethod.java
index a81726748fd..158b31ac766 100644
--- a/src/main/java/spoon/reflect/declaration/CtMethod.java
+++ b/src/main/java/spoon/reflect/declaration/CtMethod.java
@@ -20,6 +20,15 @@
* This element defines a method declaration.
*/
public interface CtMethod extends CtExecutable, CtTypeMember, CtFormalTypeDeclarer, CtShadowable {
+
+ /**
+ * The same signature is the necessary condition for method A overrides method B.
+ * @param thatMethod - the compared method
+ * @param canTypeErasure - use true if the type erasure can be used to match signatures
+ * @return true if this method and `thatMethod` has same signature
+ */
+ boolean isSameSignature(CtMethod> thatMethod, boolean canTypeErasure);
+
/**
* Checks if the method is a default method. Default method can be in interfaces from
* Java 8: http://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html.
diff --git a/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java b/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java
index cc41125d64b..95d35868831 100644
--- a/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java
+++ b/src/main/java/spoon/support/reflect/declaration/CtMethodImpl.java
@@ -20,7 +20,9 @@
import spoon.reflect.declaration.CtFormalTypeDeclarer;
import spoon.reflect.declaration.CtMethod;
import spoon.reflect.declaration.CtModifiable;
+import spoon.reflect.declaration.CtParameter;
import spoon.reflect.declaration.CtShadowable;
+import spoon.reflect.declaration.CtType;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.ModifierKind;
@@ -47,8 +49,6 @@ public class CtMethodImpl extends CtExecutableImpl implements CtMethod
boolean defaultMethod = false;
- List formalTypeParameters = emptyList();
-
List formalCtTypeParameters = emptyList();
Set modifiers = CtElementImpl.emptySet();
@@ -189,6 +189,98 @@ public void replace(CtMethod element) {
replace((CtElement) element);
}
+ @Override
+ public boolean isSameSignature(CtMethod> thatMethod, boolean canTypeErasure) {
+ //https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.2
+ if (this == thatMethod) {
+ return true;
+ }
+ if ((thatMethod instanceof CtMethod) == false) {
+ return false;
+ }
+ if (thatMethod.getSimpleName().equals(getSimpleName()) == false) {
+ return false;
+ }
+ List> thatParameters = thatMethod.getParameters();
+ if (parameters.size() != thatParameters.size()) {
+ //the methods has different count of parameters they cannot override each other
+ return false;
+ }
+ List thatTypeParameters = thatMethod.getFormalCtTypeParameters();
+ if (formalCtTypeParameters.size() == thatTypeParameters.size()) {
+ //the methods has same count of formal parameters
+ //check that formal type parameters are same
+ for (int i = 0; i < formalCtTypeParameters.size(); i++) {
+ if (CtTypeParameterImpl.isSame(formalCtTypeParameters.get(i).getReference(), thatTypeParameters.get(i).getReference(), canTypeErasure, false) == false) {
+ return false;
+ }
+ }
+ } else {
+ //the methods has different count of formal type parameters. It still can override when one of the method is without type parameters = is not generic
+ if (formalCtTypeParameters.isEmpty() == false && thatTypeParameters.isEmpty() == false) {
+ //Both methods has some parameters, but different. The signature is not same
+ return false;
+ }
+ }
+ CtType> thisDeclType = getDeclaringType();
+ CtType> thatDeclType = thatMethod.getDeclaringType();
+ CtTypeReference>[] thisParameterTypes = getParameterTypes(parameters);
+ CtTypeReference>[] thatParameterTypes = getParameterTypes(thatParameters);
+ if (thisDeclType != thatDeclType) {
+ if (thisDeclType.isSubtypeOf(thatDeclType.getReference())) {
+ //adapt parameter types of thatMethod to thisDeclType
+ if (adaptTypes(thatParameterTypes, this) == false) {
+ //cannot adapt types, the signature is not same
+ return false;
+ }
+ } else {
+ //adapt parameter types of this method to thatDeclType
+ if (adaptTypes(thisParameterTypes, thatMethod) == false) {
+ //cannot adapt types, the signature is not same
+ return false;
+ }
+ }
+ }
+ //check that parameters are same after adapted to same scope
+ for (int i = 0; i < thisParameterTypes.length; i++) {
+ CtTypeReference> thisType = thisParameterTypes[i];
+ CtTypeReference> thatType = thatParameterTypes[i];
+ if (CtTypeParameterImpl.isSame(thisType, thatType, canTypeErasure, false) == false) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * adapt all CtTypeParameterReference to targetType
+ * @param types list of to be adapted types
+ * @param targetType target type
+ * @return false if adaptation is not possible
+ */
+ private static boolean adaptTypes(CtTypeReference>[] types, CtFormalTypeDeclarer targetType) {
+ for (int i = 0; i < types.length; i++) {
+ CtTypeReference> ref = types[i];
+ if (ref instanceof CtTypeParameterReference) {
+ CtTypeParameterReference typeParamRef = (CtTypeParameterReference) ref;
+ CtTypeReference> adaptedType = typeParamRef.getDeclaration().getTypeAdaptedTo(targetType);
+ if (adaptedType == null) {
+ return false;
+ }
+ types[i] = adaptedType;
+ }
+ }
+ return true;
+ }
+
+ private static CtTypeReference>[] getParameterTypes(List> params) {
+ CtTypeReference>[] types = new CtTypeReference[params.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = params.get(i).getType();
+ }
+ return types;
+ }
+
boolean isShadow;
@Override
diff --git a/src/main/java/spoon/support/reflect/declaration/CtTypeParameterImpl.java b/src/main/java/spoon/support/reflect/declaration/CtTypeParameterImpl.java
index 54c2cfee1f7..7dcd256c2b6 100644
--- a/src/main/java/spoon/support/reflect/declaration/CtTypeParameterImpl.java
+++ b/src/main/java/spoon/support/reflect/declaration/CtTypeParameterImpl.java
@@ -248,7 +248,34 @@ public List> getDeclaredFields() {
@Override
public boolean isSubtypeOf(CtTypeReference> type) {
- return getReference().isSubtypeOf(type);
+ if (type instanceof CtTypeParameterReference) {
+ //the type is type parameter too. Use appropriate sub type checking algorithm
+ CtTypeParameter subTypeParam = (CtTypeParameter) type.getDeclaration();
+ return isSubtypeOf(this, subTypeParam);
+ }
+ //type is normal type
+ return getTypeErasure().isSubtypeOf(type);
+ }
+
+ private static boolean isSubtypeOf(CtTypeParameter superTypeParam, CtTypeParameter subTypeParam) {
+ while (superTypeParam != null) {
+ if (isSame(superTypeParam, subTypeParam, false, true)) {
+ //both type params are same
+ return true;
+ }
+ CtTypeReference> superType = superTypeParam.getSuperclass();
+ if (superType == null) {
+ //there is no super type defined
+ return false;
+ }
+ if (superType instanceof CtTypeParameterReference) {
+ superTypeParam = ((CtTypeParameterReference) superType).getDeclaration();
+ } else {
+ //the super type is not type parameter
+ return false;
+ }
+ }
+ return false;
}
@Override
@@ -303,6 +330,146 @@ public CtTypeReference> getTypeAdaptedTo(CtFormalTypeDeclarer targetScope) {
}
}
+ static boolean isSame(CtTypeParameter thisType, CtTypeParameter thatType, boolean canTypeErasure, boolean checkMethodOverrides) {
+ CtFormalTypeDeclarer thisTypeScope = thisType.getTypeParameterDeclarer();
+ if (thisTypeScope == null) {
+ throw new SpoonException("This type parameter has no parent formal type declarer.");
+ }
+ CtFormalTypeDeclarer thatTypeScope = thatType.getTypeParameterDeclarer();
+ if (thatTypeScope == null) {
+ throw new SpoonException("The type parameter has no parent formal type declarer.");
+ }
+ if (thisTypeScope == thatTypeScope) {
+ //the scopes are same. Just check if types are same
+ return isSameInSameScope(thisType, thatType);
+ }
+ //scopes are not same.
+ if (thisTypeScope instanceof CtType) {
+ if (thatTypeScope instanceof CtType) {
+ //both are TypeParameters defined in scope of type.
+ if (((CtType>) thisTypeScope).isSubtypeOf(((CtType>) thatTypeScope).getReference())) {
+ //translate thatType parameter type to scope of thisTypeScope
+ CtTypeReference> targetTypeRef = getReferenceInScope(thatType.getReference(), (CtType>) thisTypeScope);
+ //and then check if they are same
+ return isSameInSameScope(thisType, targetTypeRef);
+ } else {
+ //translate this type parameter type to scope of thatTypeScope
+ CtTypeReference> targetTypeRef = getReferenceInScope(thisType.getReference(), (CtType>) thatTypeScope);
+ //and then check if they are same
+ return isSameInSameScope(thatType, targetTypeRef);
+ }
+ }
+ return false;
+ }
+ if (thisTypeScope instanceof CtConstructor || thatTypeScope instanceof CtConstructor) {
+ //the type parameters declared in different constructors are never same
+ return false;
+ }
+ if (thisTypeScope instanceof CtMethod && thatTypeScope instanceof CtMethod) {
+ CtMethod> thisMethod = (CtMethod>) thisTypeScope;
+ CtMethod> thatMethod = (CtMethod>) thatTypeScope;
+ /*
+ * The type parameters of generic executables are same if
+ * 1) the methods are same or if methods overrides each other.
+ * 2) they are declared on same position
+ * 3) they have same bound
+ * See https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.4
+ */
+ if (checkMethodOverrides) {
+ if (thisMethod.isSameSignature(thatMethod, false) == false) {
+ //the signature of methods is not same
+ return false;
+ }
+ CtType> thisDeclType = thisMethod.getDeclaringType();
+ CtType> thatDeclType = thatMethod.getDeclaringType();
+ if (thisDeclType == thatDeclType) {
+ //if two different methods are declared in the same type then they cannot override
+ return false;
+ }
+ if (thisDeclType.isSubtypeOf(thatDeclType.getReference()) == false && thatDeclType.isSubtypeOf(thisDeclType.getReference()) == false) {
+ //if two different methods are declared in the types which does not inherit then they cannot override
+ return false;
+ }
+ }
+ int thisPosition = getTypeParameterPosition(thisType, thisTypeScope);
+ int thatPosition = getTypeParameterPosition(thatType, thatTypeScope);
+ if (thisPosition != thatPosition) {
+ return false;
+ }
+ CtTypeReference> thisBound = getBound(thisType);
+ CtTypeReference> thatBound = getBound(thatType);
+ return isSame(thisBound, thatBound, canTypeErasure, checkMethodOverrides);
+ }
+ return false;
+ }
+
+ /**
+ * @param thisType to be compared type 1
+ * @param thatType to be compared type 2
+ * @param canTypeErasure if true and types are not same, then that type erasure is applied to types before they checked if same
+ * @param checkMethodOverrides TODO
+ * @return true if thisType and thatType are same
+ */
+ static boolean isSame(CtTypeReference> thisType, CtTypeReference> thatType, boolean canTypeErasure, boolean checkMethodOverrides) {
+ if (thisType instanceof CtTypeParameterReference) {
+ CtTypeParameterReference thisTypeParamRef = (CtTypeParameterReference) thisType;
+ CtTypeParameter thisTypeParam = thisTypeParamRef.getDeclaration();
+ if (thisTypeParam == null) {
+ return false;
+ }
+ if (thatType instanceof CtTypeParameterReference) {
+ CtTypeParameterReference thatTypeParamRef = (CtTypeParameterReference) thatType;
+ CtTypeParameter thatTypeParam = thatTypeParamRef.getDeclaration();
+ if (thatTypeParam == null) {
+ return false;
+ }
+ if (isSame(thisTypeParam, thatTypeParam, canTypeErasure, checkMethodOverrides)) {
+ return true;
+ }
+ if (canTypeErasure == false) {
+ return false;
+ }
+ thatType = thatTypeParam.getTypeErasure();
+ } else if (canTypeErasure == false) {
+ return false;
+ }
+ thisType = thisTypeParam.getTypeErasure();
+ }
+ if (canTypeErasure && thatType instanceof CtTypeParameterReference) {
+ CtTypeParameter thatTypeParam = ((CtTypeParameterReference) thatType).getDeclaration();
+ if (thatTypeParam == null) {
+ return false;
+ }
+ thatType = thatTypeParam.getTypeErasure();
+ }
+ return thisType.getQualifiedName().equals(thatType.getQualifiedName());
+ }
+
+ /**
+ * Note: This method expects that both arguments are already adapted to the same scope
+ * @param typeParam a type param 1
+ * @param typeRef a reference to some type 2
+ * @return true if typeParam and typeRef represents same type parameter.
+ */
+ private static boolean isSameInSameScope(CtTypeParameter typeParam, CtTypeReference> typeRef) {
+ if (typeRef instanceof CtTypeParameterReference) {
+ return typeParam.getSimpleName().equals(((CtTypeParameterReference) typeRef).getSimpleName());
+ }
+ return false;
+ }
+ /**
+ * Note: This method expects that both arguments are already adapted to the same scope
+ * @param typeParam a type param 1
+ * @param type a type 2
+ * @return true if typeParam and type represents same type parameter.
+ */
+ private static boolean isSameInSameScope(CtTypeParameter typeParam, CtType> type) {
+ if (type instanceof CtTypeParameter) {
+ return typeParam.getSimpleName().equals(((CtTypeParameter) type).getSimpleName());
+ }
+ return false;
+ }
+
/**
* Converts potentially generic parameters from namespace of some super class to namespace of `targetScope`
* @param type - the reference to to be converted parameter (generic) type or normal type.
diff --git a/src/test/java/spoon/test/ctType/CtTypeParameterTest.java b/src/test/java/spoon/test/ctType/CtTypeParameterTest.java
index e5b6332e255..a622cd88c0f 100644
--- a/src/test/java/spoon/test/ctType/CtTypeParameterTest.java
+++ b/src/test/java/spoon/test/ctType/CtTypeParameterTest.java
@@ -2,6 +2,8 @@
import java.lang.reflect.Executable;
import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.util.List;
import org.junit.Test;
@@ -16,6 +18,7 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.visitor.filter.NameFilter;
+import spoon.support.reflect.declaration.CtTypeParameterImpl;
import spoon.test.ctType.testclasses.ErasureModelA;
import spoon.testing.utils.ModelUtils;
@@ -108,4 +111,88 @@ public void testTypeAdapted() throws Exception {
assertEquals("java.lang.IllegalArgumentException", tpC.getTypeAdaptedTo(ctModelC).getQualifiedName());
assertEquals("spoon.test.ctType.testclasses.ErasureModelA$ModelC", tpD.getTypeAdaptedTo(ctModelC).getQualifiedName());
}
+
+ @Test
+ public void testTypeSame() throws Exception {
+ CtClass> ctModel = (CtClass>) ModelUtils.buildClass(ErasureModelA.class);
+ CtTypeParameter tpA = ctModel.getFormalCtTypeParameters().get(0);
+ CtTypeParameter tpB = ctModel.getFormalCtTypeParameters().get(1);
+ CtTypeParameter tpC = ctModel.getFormalCtTypeParameters().get(2);
+ CtTypeParameter tpD = ctModel.getFormalCtTypeParameters().get(3);
+
+ CtConstructor> ctModelCons = ctModel.getConstructors().iterator().next();
+ CtMethod> ctModelMethod = ctModel.getMethodsByName("method").get(0);
+ CtMethod> ctModelMethod2 = ctModel.getMethodsByName("method2").get(0);
+
+ CtClass> ctModelB = ctModel.filterChildren(new NameFilter<>("ModelB")).first();
+ CtTypeParameter tpA2 = ctModelB.getFormalCtTypeParameters().get(0);
+ CtTypeParameter tpB2 = ctModelB.getFormalCtTypeParameters().get(1);
+ CtTypeParameter tpC2 = ctModelB.getFormalCtTypeParameters().get(2);
+ CtTypeParameter tpD2 = ctModelB.getFormalCtTypeParameters().get(3);
+
+ CtConstructor> ctModelBCons = ctModelB.getConstructors().iterator().next();
+ CtMethod> ctModelBMethod = ctModelB.getMethodsByName("method").get(0);
+
+ //the type parameters of ErasureModelA and ErasureModelA$ModelB are same if they are on the same position.
+ checkIsSame(ctModel.getFormalCtTypeParameters(), ctModelB.getFormalCtTypeParameters(), true);
+
+ //the type parameters of ErasureModelA#constructor and ErasureModelA$ModelB constructor are NOT same, because constructors does not override
+ checkIsSame(ctModelCons.getFormalCtTypeParameters(), ctModelBCons.getFormalCtTypeParameters(), false);
+ //the type parameters of ctModel ErasureModelA#method and ErasureModelA$ModelB#method are same if they are on the same position.
+ checkIsSame(ctModelMethod.getFormalCtTypeParameters(), ctModelBMethod.getFormalCtTypeParameters(), true);
+
+ //the type parameters of ctModel ErasureModelA#constructor and ErasureModelA$ModelB#method are never same, because they have different type of scope (Method!=Constructor)
+ checkIsSame(ctModelCons.getFormalCtTypeParameters(), ctModelBMethod.getFormalCtTypeParameters(), false);
+ //the type parameters of ctModel ErasureModelA#method and ErasureModelA#method2 are never same, because we can be sure that method does not override method2
+ checkIsSame(ctModelMethod.getFormalCtTypeParameters(), ctModelMethod2.getFormalCtTypeParameters(), false);
+
+ CtClass> ctModelC = ctModel.filterChildren(new NameFilter<>("ModelC")).first();
+ }
+
+ /**
+ * checks that parameters on the same position are same and parameters on other positions are not same
+ * @param isSameOnSameIndex TODO
+ */
+ private void checkIsSame(List tps1, List tps2, boolean isSameOnSameIndex) {
+ for(int i=0; i xClass = launcher.getFactory().Class().get("spoon.test.ctType.testclasses.X");
@@ -73,8 +73,9 @@ public void testHasMethodInDefaultMethod() throws Exception {
@Test
public void testIsSubTypeOf() throws Exception {
- CtType xCtType = buildClass(X.class);
- CtType> yCtType = xCtType.getFactory().Type().get("spoon.test.ctType.testclasses.Y");
+ Factory factory = build(new File("./src/test/java/spoon/test/ctType/testclasses"));
+ CtType xCtType = factory.Type().get(X.class);
+ CtType> yCtType = factory.Type().get("spoon.test.ctType.testclasses.Y");
assertFalse(xCtType.isSubtypeOf(yCtType.getReference()));
assertTrue(yCtType.isSubtypeOf(xCtType.getReference()));
@@ -84,27 +85,53 @@ public void testIsSubTypeOf() throws Exception {
//using CtType implementation
assertTrue(xCtType.isSubtypeOf(xCtType.getReference()));
}
+
+ @Test
+ public void testXModelConsistency() throws Exception {
+ Q q = new Q(null);
+ q.m3((Y)null);
+
+ R r = new R(null);
+ r.m3((Object)null);
+ r.m3((X)null);
+ r.m3((Y)null);
+ }
@Test
public void testIsSubTypeOfonTypeParameters() throws Exception {
- CtType xCtType = buildClass(X.class);
-
- CtType> oCtType = xCtType.getFactory().Type().get("spoon.test.ctType.testclasses.O");
-
- List typeParameters = oCtType.getFormalCtTypeParameters();
- assertTrue(typeParameters.size() == 1);
-
- CtType> aCtType = typeParameters.get(0);
-
- List> methods = oCtType.getMethodsByName("foo");
-
- assertTrue(methods.size() == 1);
-
- CtMethod> fooMethod = methods.get(0);
- CtType> bCtType = fooMethod.getType().getDeclaration();
-
- //The TypeParameters are not supported yet
-// assertTrue(bCtType.isSubtypeOf(xCtType.getReference()));
-// assertTrue(bCtType.isSubtypeOf(aCtType.getReference()));
+ Factory factory = build(new File("./src/test/java/spoon/test/ctType/testclasses"));
+ CtType xCtType = factory.Type().get(X.class);
+
+ CtType> oCtType = factory.Type().get("spoon.test.ctType.testclasses.O");
+ CtType> pCtType = factory.Type().get("spoon.test.ctType.testclasses.P");
+ CtTypeReference> objectCtTypeRef = factory.Type().OBJECT;
+
+ List oTypeParameters = oCtType.getFormalCtTypeParameters();
+ assertTrue(oTypeParameters.size() == 1);
+ List pTypeParameters = pCtType.getFormalCtTypeParameters();
+ assertTrue(pTypeParameters.size() == 2);
+
+ CtType> O_A_CtType = oTypeParameters.get(0);
+ CtType> P_D_CtType = pTypeParameters.get(0);
+ CtType> P_F_CtType = pTypeParameters.get(1);
+
+ CtMethod> O_FooMethod = oCtType.filterChildren(new NameFilter<>("foo")).first();
+ CtMethod> P_FooMethod = pCtType.filterChildren(new NameFilter<>("foo")).first();
+
+ CtType> O_B_CtType = O_FooMethod.getType().getDeclaration();
+ CtType> P_E_CtType = P_FooMethod.getType().getDeclaration();
+
+ assertTrue(O_B_CtType.isSubtypeOf(xCtType.getReference()));
+ assertTrue(O_B_CtType.isSubtypeOf(O_A_CtType.getReference()));
+
+ assertTrue(P_E_CtType.isSubtypeOf(xCtType.getReference()));
+ assertTrue(P_E_CtType.isSubtypeOf(P_D_CtType.getReference()));
+ assertTrue(P_E_CtType.isSubtypeOf(O_A_CtType.getReference()));
+
+ assertTrue(P_D_CtType.isSubtypeOf(O_A_CtType.getReference()));
+ assertTrue(P_E_CtType.isSubtypeOf(O_B_CtType.getReference()));
+
+ assertTrue(P_E_CtType.isSubtypeOf(objectCtTypeRef));
+ assertTrue(P_F_CtType.isSubtypeOf(objectCtTypeRef));
}
}
diff --git a/src/test/java/spoon/test/ctType/testclasses/ErasureModelA.java b/src/test/java/spoon/test/ctType/testclasses/ErasureModelA.java
index 6a011aed597..7f74a908dfb 100644
--- a/src/test/java/spoon/test/ctType/testclasses/ErasureModelA.java
+++ b/src/test/java/spoon/test/ctType/testclasses/ErasureModelA.java
@@ -13,6 +13,9 @@ public ErasureModelA(I paramI, J paramJ, D paramD) {
public void method(I paramI, J paramJ, D paramD) {
}
+ public void method2(I paramI, J paramJ, D paramD) {
+ }
+
static class ModelB> extends ErasureModelA {
A2 paramA2;
B2 paramB2;
@@ -24,7 +27,7 @@ public ModelB(I paramI, J paramJ, D2 paramD2) {
}
@Override
- public void method(I paramI, J paramJ, D2 paramD2) {
+ public void method(I2 paramI2, J2 paramJ2, D2 paramD2) {
}
}
diff --git a/src/test/java/spoon/test/ctType/testclasses/O.java b/src/test/java/spoon/test/ctType/testclasses/O.java
new file mode 100644
index 00000000000..9ce0c98b4b6
--- /dev/null
+++ b/src/test/java/spoon/test/ctType/testclasses/O.java
@@ -0,0 +1,21 @@
+package spoon.test.ctType.testclasses;
+
+import java.io.InputStream;
+
+public class O {
+ O(B b) {}
+
+ B foo() {
+ return null;
+ }
+
+ void m1(B b) {}
+ void m2(B b) {}
+ int m3(B b) {return 0;}
+ //this method m3#(A) will cause conflict with next one m3(Y) after A is substituted by Y in class Q.
+ int m3(B b) {return 1;}
+ int m3(B b) {return 2;}
+ int m3(B b) {return 4;}
+
+ void m6(A p1, B p2) {}
+}
\ No newline at end of file
diff --git a/src/test/java/spoon/test/ctType/testclasses/P.java b/src/test/java/spoon/test/ctType/testclasses/P.java
new file mode 100644
index 00000000000..99e225911af
--- /dev/null
+++ b/src/test/java/spoon/test/ctType/testclasses/P.java
@@ -0,0 +1,30 @@
+package spoon.test.ctType.testclasses;
+
+import java.io.InputStream;
+
+import org.junit.Assert;
+
+public class P extends O {
+ P(E b) {
+ super(b);
+ }
+ @Override
+ E foo() {
+ return null;
+ }
+ void m1(B b) {}
+ void m2(B b) {}
+ int m3(B b) {
+ Assert.assertEquals(0, super.m3(b));
+ return 5;
+ }
+// @Override
+// void m3(B b) {}
+ void m5(D b) {}
+
+ //this is not compilable
+// void m6(A p1, Throwable p2) {}
+ @Override
+ void m6(InputStream p1, Throwable p2) {}
+
+}
\ No newline at end of file
diff --git a/src/test/java/spoon/test/ctType/testclasses/Q.java b/src/test/java/spoon/test/ctType/testclasses/Q.java
new file mode 100644
index 00000000000..4cef8147b81
--- /dev/null
+++ b/src/test/java/spoon/test/ctType/testclasses/Q.java
@@ -0,0 +1,38 @@
+package spoon.test.ctType.testclasses;
+
+import org.junit.Assert;
+
+public class Q extends O {
+ public Q(Y b) {
+ super(b);
+ }
+ @Override
+ public Y foo() {
+ return null;
+ }
+ @Override
+ public void m1(Y b) {}
+ public void m2(Object b) {}
+ @Override
+ public void m2(Y b) {}
+ @Override
+ public int m3(Object b) {
+ Assert.assertEquals(0, super.m3(b));
+ return 6;
+ }
+// void m3(X b) { //name clash compilation error
+// }
+ @Override
+ public int m3(Y b) {
+// super.m3(b);//m3(X) and m3(Y) are ambiguous
+ Assert.assertEquals(0, super.m3((Object)b)); //calls m3(Object)
+ Assert.assertEquals(0, super.m3((X)b)); //calls m3(Object)
+// Assert.assertEquals(?, super.m3((Y)b)); //m3(X) and m3(Y) are ambiguous. Cannot compile
+ return 7;
+ }
+ @Override
+ public int m3(Integer b) {
+ Assert.assertEquals(4, super.m3(b));
+ return 8;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/spoon/test/ctType/testclasses/R.java b/src/test/java/spoon/test/ctType/testclasses/R.java
new file mode 100644
index 00000000000..69644e48a07
--- /dev/null
+++ b/src/test/java/spoon/test/ctType/testclasses/R.java
@@ -0,0 +1,26 @@
+package spoon.test.ctType.testclasses;
+
+import org.junit.Assert;
+
+public class R extends O {
+ public R(X b) {
+ super(b);
+ }
+ @Override
+ public int m3(Object b) {
+ Assert.assertEquals(0, super.m3(b));//calls m3(Object)
+ return 9;
+ }
+ public int m3(X b) { //name clash compilation error
+ Assert.assertEquals(0, super.m3((Object)b));//calls m3(Object)
+ Assert.assertEquals(1, super.m3(b));//calls m3(X)
+ Assert.assertEquals(1, super.m3((X)b));//calls m3(X)
+ Assert.assertEquals(2, super.m3((Y)b));//calls m3(Y)
+ return 10;
+ }
+ @Override
+ public int m3(Y b) {
+ Assert.assertEquals(2, super.m3((Y)b));//calls m3(Y)
+ return 11;
+ }
+}
\ No newline at end of file
diff --git a/src/test/java/spoon/test/ctType/testclasses/X.java b/src/test/java/spoon/test/ctType/testclasses/X.java
index 60266fb5297..36bc6d71370 100644
--- a/src/test/java/spoon/test/ctType/testclasses/X.java
+++ b/src/test/java/spoon/test/ctType/testclasses/X.java
@@ -6,20 +6,9 @@ public void foo() {
}
}
-class Y extends X {
-
-}
-
interface Z {
void foo();
}
abstract class W implements Z {
}
-
-class O {
- B foo() {
- return null;
- }
-}
-
diff --git a/src/test/java/spoon/test/ctType/testclasses/Y.java b/src/test/java/spoon/test/ctType/testclasses/Y.java
new file mode 100644
index 00000000000..96d79092e13
--- /dev/null
+++ b/src/test/java/spoon/test/ctType/testclasses/Y.java
@@ -0,0 +1,5 @@
+package spoon.test.ctType.testclasses;
+
+public class Y extends X {
+
+}
\ No newline at end of file