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