diff --git a/src/main/java/spoon/support/visitor/ClassTypingContext.java b/src/main/java/spoon/support/visitor/ClassTypingContext.java index 36465157f11..cdecd900e11 100644 --- a/src/main/java/spoon/support/visitor/ClassTypingContext.java +++ b/src/main/java/spoon/support/visitor/ClassTypingContext.java @@ -469,36 +469,52 @@ private List> resolveTypeParameters(List> CtTypeParameterReference typeParamRef = (CtTypeParameterReference) typeRef; CtTypeParameter typeParam = typeParamRef.getDeclaration(); CtFormalTypeDeclarer declarer = typeParam.getTypeParameterDeclarer(); - if ((declarer instanceof CtType) == false) { - throw new SpoonException("Cannot adapt type parameters of non type scope"); - } - CtType typeDeclarer = (CtType) declarer; - List> actualTypeArguments = getActualTypeArguments(typeDeclarer.getQualifiedName()); - if (actualTypeArguments == null) { - /* - * the actualTypeArguments of this declarer cannot be resolved. - * There is probably a model inconsistency - */ - throw new SpoonException("Cannot resolve " + (result.size() + 1) + ") type parameter <" + typeParamRef.getSimpleName() + "> of declarer " + declarer); - } - if (actualTypeArguments.size() != typeDeclarer.getFormalCtTypeParameters().size()) { - if (actualTypeArguments.isEmpty() == false) { - throw new SpoonException("Unexpected actual type arguments " + actualTypeArguments + " on " + typeDeclarer); - } - /* - * the scope type was delivered as type reference without appropriate type arguments. - * Use references to formal type parameters - */ - actualTypeArguments = getTypeReferences(typeDeclarer.getFormalCtTypeParameters()); - typeToArguments.put(typeDeclarer.getQualifiedName(), actualTypeArguments); - } - typeRef = getValue(actualTypeArguments, typeParam, declarer); + typeRef = resolveTypeParameter(declarer, typeParamRef, typeParam, typeRef); } result.add(typeRef); } return result; } + private CtTypeReference resolveTypeParameter(CtFormalTypeDeclarer declarer, CtTypeParameterReference typeParamRef, CtTypeParameter typeParam, CtTypeReference typeRef) { + if ((declarer instanceof CtType) == false) { + /* + * The declarer is probably out of the scope of this ClassTypingContext. + * For example outer class or method declares type parameter, + * which is then used as argument in inner class, whose ClassTypingContext we have now + * See GenericsTest#testCannotAdaptTypeOfNonTypeScope. + * + * Use that outer type parameter reference directly without adaptation + */ + return typeRef; + } + CtType typeDeclarer = (CtType) declarer; + List> actualTypeArguments = getActualTypeArguments(typeDeclarer.getQualifiedName()); + if (actualTypeArguments == null) { + /* + * The declarer is probably out of the scope of this ClassTypingContext. + * For example outer class or method declares type parameter, + * which is then used as argument in inner class, whose ClassTypingContext we have now + * See GenericsTest#testCannotAdaptTypeOfNonTypeScope. + * + * Use that outer type parameter reference directly without adaptation + */ + return typeRef; + } + if (actualTypeArguments.size() != typeDeclarer.getFormalCtTypeParameters().size()) { + if (actualTypeArguments.isEmpty() == false) { + throw new SpoonException("Unexpected actual type arguments " + actualTypeArguments + " on " + typeDeclarer); + } + /* + * the scope type was delivered as type reference without appropriate type arguments. + * Use references to formal type parameters + */ + actualTypeArguments = getTypeReferences(typeDeclarer.getFormalCtTypeParameters()); + typeToArguments.put(typeDeclarer.getQualifiedName(), actualTypeArguments); + } + return getValue(actualTypeArguments, typeParam, declarer); + } + private List> getActualTypeArguments(String qualifiedName) { List> actualTypeArguments = typeToArguments.get(qualifiedName); if (actualTypeArguments != null) { diff --git a/src/test/java/spoon/test/generics/GenericsTest.java b/src/test/java/spoon/test/generics/GenericsTest.java index 233750bb484..af0e26f7a5f 100644 --- a/src/test/java/spoon/test/generics/GenericsTest.java +++ b/src/test/java/spoon/test/generics/GenericsTest.java @@ -2,7 +2,6 @@ import org.junit.Test; import spoon.Launcher; -import spoon.MavenLauncher; import spoon.SpoonModelBuilder; import spoon.compiler.SpoonResourceHelper; import spoon.reflect.code.BinaryOperatorKind; @@ -11,6 +10,7 @@ import spoon.reflect.code.CtInvocation; import spoon.reflect.code.CtLocalVariable; import spoon.reflect.code.CtNewClass; +import spoon.reflect.code.CtReturn; import spoon.reflect.declaration.CtAnnotation; import spoon.reflect.declaration.CtClass; import spoon.reflect.declaration.CtConstructor; @@ -56,6 +56,7 @@ import spoon.test.generics.testclasses.Lunch; import spoon.test.generics.testclasses.Mole; import spoon.test.generics.testclasses.Orange; +import spoon.test.generics.testclasses.OuterTypeParameter; import spoon.test.generics.testclasses.Paella; import spoon.test.generics.testclasses.Panini; import spoon.test.generics.testclasses.SameSignature; @@ -1403,4 +1404,19 @@ public void testIsGenericTypeEqual() { MainTest.checkParentConsistency(launcher.getFactory().getModel().getRootPackage()); MainTest.checkParentConsistency(adaptedMethod); } + + @Test + public void testCannotAdaptTypeOfNonTypeScope() throws Exception { + //contract: ClassTypingContext doesn't fail on type parameters, which are defined out of the scope of ClassTypingContext + CtType ctClass = ModelUtils.buildClass(OuterTypeParameter.class); + //the method defines type parameter, which is used in super of local class + CtReturn retStmt = (CtReturn) ctClass.getMethodsByName("method").get(0).getBody().getStatements().get(0); + CtNewClass newClassExpr = (CtNewClass) retStmt.getReturnedExpression(); + CtType declaringType = newClassExpr.getAnonymousClass(); + CtMethod m1 = declaringType.getMethodsByName("iterator").get(0); + ClassTypingContext c = new ClassTypingContext(declaringType); + //the adaptation of such type parameter keeps that parameter as it is. + assertFalse(c.isOverriding(m1, declaringType.getSuperclass().getTypeDeclaration().getMethodsByName("add").get(0))); + assertTrue(c.isOverriding(m1, declaringType.getSuperclass().getTypeDeclaration().getMethodsByName("iterator").get(0))); + } } diff --git a/src/test/java/spoon/test/generics/testclasses/OuterTypeParameter.java b/src/test/java/spoon/test/generics/testclasses/OuterTypeParameter.java new file mode 100644 index 00000000000..0adf39f1f79 --- /dev/null +++ b/src/test/java/spoon/test/generics/testclasses/OuterTypeParameter.java @@ -0,0 +1,16 @@ +package spoon.test.generics.testclasses; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class OuterTypeParameter { + public List method() { + return new ArrayList() { + @Override + public Iterator iterator() { + return super.iterator(); + } + }; + } +}