diff --git a/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java b/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java index d6775bc4c4e..270d26dec95 100644 --- a/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java +++ b/src/main/java/spoon/reflect/visitor/CtBiScannerDefault.java @@ -14,8 +14,6 @@ * The fact that you are presently reading this means that you have had * knowledge of the CeCILL-C license and that you accept its terms. */ - - package spoon.reflect.visitor; @@ -437,7 +435,6 @@ public void visitCtCatchVariable(final spoon.reflect.code.CtCatchVariable biScan(catchVariable.getComments(), other.getComments()); biScan(catchVariable.getAnnotations(), other.getAnnotations()); biScan(catchVariable.getDefaultExpression(), other.getDefaultExpression()); - biScan(catchVariable.getType(), other.getType()); biScan(catchVariable.getMultiTypes(), other.getMultiTypes()); exit(catchVariable); } diff --git a/src/main/java/spoon/reflect/visitor/CtScanner.java b/src/main/java/spoon/reflect/visitor/CtScanner.java index 0b2adcd671a..adf10d472ab 100644 --- a/src/main/java/spoon/reflect/visitor/CtScanner.java +++ b/src/main/java/spoon/reflect/visitor/CtScanner.java @@ -513,7 +513,6 @@ public void visitCtCatchVariable(final CtCatchVariable catchVariable) { scan(catchVariable.getComments()); scan(catchVariable.getAnnotations()); scan(catchVariable.getDefaultExpression()); - scan(catchVariable.getType()); scan(catchVariable.getMultiTypes()); exit(catchVariable); } diff --git a/src/main/java/spoon/support/compiler/jdt/ContextBuilder.java b/src/main/java/spoon/support/compiler/jdt/ContextBuilder.java index 1142ee7012a..5b1efb55a23 100644 --- a/src/main/java/spoon/support/compiler/jdt/ContextBuilder.java +++ b/src/main/java/spoon/support/compiler/jdt/ContextBuilder.java @@ -105,7 +105,7 @@ void enter(CtElement e, ASTNode node) { } try { - if (e instanceof CtTypedElement && !(e instanceof CtConstructorCall) && node instanceof Expression) { + if (e instanceof CtTypedElement && !(e instanceof CtConstructorCall) && !(e instanceof CtCatchVariable) && node instanceof Expression) { if (((CtTypedElement) e).getType() == null) { ((CtTypedElement) e).setType(this.jdtTreeBuilder.getReferencesBuilder().getTypeReference(((Expression) node).resolvedType)); } diff --git a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java index 3ade6c1bd44..b79d4360fe6 100644 --- a/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java +++ b/src/main/java/spoon/support/compiler/jdt/JDTTreeBuilderHelper.java @@ -25,6 +25,7 @@ import org.eclipse.jdt.internal.compiler.ast.SingleNameReference; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeReference; +import org.eclipse.jdt.internal.compiler.ast.UnionTypeReference; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.lookup.FieldBinding; import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding; @@ -112,10 +113,17 @@ static String createQualifiedTypeName(char[][] typeName) { CtCatchVariable createCatchVariable(TypeReference typeReference) { final Argument jdtCatch = (Argument) jdtTreeBuilder.getContextBuilder().stack.peekFirst().node; final Set modifiers = getModifiers(jdtCatch.modifiers); - return jdtTreeBuilder.getFactory().Code().createCatchVariable(// - jdtTreeBuilder.getReferencesBuilder().getTypeReference(typeReference.resolvedType), // - CharOperation.charToString(jdtCatch.name), // - modifiers.toArray(new ModifierKind[modifiers.size()])); + if (typeReference instanceof UnionTypeReference) { + return jdtTreeBuilder.getFactory().Code().createCatchVariable(// + null, //do not set type of variable yet. It will be initialized later by visit of multiple types. Each call then ADDs one type + CharOperation.charToString(jdtCatch.name), // + modifiers.toArray(new ModifierKind[modifiers.size()])); + } else { + return jdtTreeBuilder.getFactory().Code().createCatchVariable(// + jdtTreeBuilder.getReferencesBuilder().getTypeReference(typeReference.resolvedType), // + CharOperation.charToString(jdtCatch.name), // + modifiers.toArray(new ModifierKind[modifiers.size()])); + } } /** diff --git a/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java b/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java index eeda9f9ef4f..8f69edec19d 100644 --- a/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java +++ b/src/main/java/spoon/support/reflect/code/CtCatchVariableImpl.java @@ -29,11 +29,13 @@ import spoon.reflect.reference.CtCatchVariableReference; import spoon.reflect.reference.CtTypeReference; import spoon.reflect.visitor.CtVisitor; +import spoon.reflect.visitor.filter.SuperInheritanceHierarchyFunction; import spoon.support.DerivedProperty; import spoon.support.UnsettableProperty; import spoon.support.reflect.declaration.CtElementImpl; import java.util.ArrayList; +import java.util.Collections; import java.util.EnumSet; import java.util.HashSet; import java.util.List; @@ -50,9 +52,6 @@ public class CtCatchVariableImpl extends CtCodeElementImpl implements CtCatch @MetamodelPropertyField(role = CtRole.NAME) String name = ""; - @MetamodelPropertyField(role = CtRole.TYPE) - CtTypeReference type; - @MetamodelPropertyField(role = CtRole.TYPE) List> types = emptyList(); @@ -80,9 +79,35 @@ public String getSimpleName() { return name; } + @SuppressWarnings("unchecked") @Override + @DerivedProperty public CtTypeReference getType() { - return type; + if (types.isEmpty()) { + return null; + } else if (types.size() == 1) { + return (CtTypeReference) types.get(0); + } + //compute common super type of exceptions + List> superTypesOfFirst = types.get(0).map(new SuperInheritanceHierarchyFunction() + .includingInterfaces(false) + .includingSelf(true) + .returnTypeReferences(true)).list(); + int commonSuperTypeIdx = 0; + //index of Throwable. Last is Object + int throwableIdx = superTypesOfFirst.size() - 2; + for (int i = 1; i < types.size() && commonSuperTypeIdx != throwableIdx; i++) { + CtTypeReference nextException = types.get(i); + while (commonSuperTypeIdx < throwableIdx) { + if (nextException.isSubtypeOf(superTypesOfFirst.get(commonSuperTypeIdx))) { + //nextException is sub type of actually selected commonSuperType + break; + } + //try next super type + commonSuperTypeIdx++; + } + } + return (CtTypeReference) superTypesOfFirst.get(commonSuperTypeIdx); } @Override @@ -101,11 +126,7 @@ public C setSimpleName(String simpleName) { @Override public C setType(CtTypeReference type) { - if (type != null) { - type.setParent(this); - } - getFactory().getEnvironment().getModelChangeListener().onObjectUpdate(this, TYPE, type, this.type); - this.type = type; + setMultiTypes(type == null ? emptyList() : Collections.singletonList(type)); return (C) this; } diff --git a/src/main/java/spoon/support/visitor/clone/CloneVisitor.java b/src/main/java/spoon/support/visitor/clone/CloneVisitor.java index a483e0ef9af..7243cd6ff43 100644 --- a/src/main/java/spoon/support/visitor/clone/CloneVisitor.java +++ b/src/main/java/spoon/support/visitor/clone/CloneVisitor.java @@ -408,7 +408,6 @@ public void visitCtCatchVariable(final spoon.reflect.code.CtCatchVariable aCtCatchVariable.setComments(this.cloneHelper.clone(catchVariable.getComments())); aCtCatchVariable.setAnnotations(this.cloneHelper.clone(catchVariable.getAnnotations())); aCtCatchVariable.setDefaultExpression(this.cloneHelper.clone(catchVariable.getDefaultExpression())); - aCtCatchVariable.setType(this.cloneHelper.clone(catchVariable.getType())); aCtCatchVariable.setMultiTypes(this.cloneHelper.clone(catchVariable.getMultiTypes())); this.other = aCtCatchVariable; } diff --git a/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java b/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java index caa26af3aa2..0ccbf87031b 100644 --- a/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java +++ b/src/main/java/spoon/support/visitor/replace/ReplacementVisitor.java @@ -23,6 +23,12 @@ * This class is generated automatically by the processor {@link spoon.generating.ReplacementVisitorGenerator}. */ public class ReplacementVisitor extends spoon.reflect.visitor.CtScanner { + private spoon.reflect.declaration.CtElement original; + + private spoon.reflect.declaration.CtElement[] replace; + + private static final spoon.reflect.declaration.CtElement[] EMPTY = new spoon.reflect.declaration.CtElement[0]; + public static void replace(spoon.reflect.declaration.CtElement original, spoon.reflect.declaration.CtElement replace) { try { new spoon.support.visitor.replace.ReplacementVisitor(original, (replace == null ? spoon.support.visitor.replace.ReplacementVisitor.EMPTY : new spoon.reflect.declaration.CtElement[]{ replace })).scan(original.getParent()); @@ -41,12 +47,6 @@ public static void replace(spoon } } - private spoon.reflect.declaration.CtElement original; - - private spoon.reflect.declaration.CtElement[] replace; - - private static final spoon.reflect.declaration.CtElement[] EMPTY = new spoon.reflect.declaration.CtElement[0]; - private ReplacementVisitor(spoon.reflect.declaration.CtElement original, spoon.reflect.declaration.CtElement... replace) { this.original = original; this.replace = (replace == null) ? spoon.support.visitor.replace.ReplacementVisitor.EMPTY : replace; @@ -1234,7 +1234,6 @@ public void visitCtCatchVariable(final spoon.reflect.code.CtCatchVariable replaceInListIfExist(catchVariable.getComments(), new spoon.support.visitor.replace.ReplacementVisitor.CtElementCommentsReplaceListener(catchVariable)); replaceInListIfExist(catchVariable.getAnnotations(), new spoon.support.visitor.replace.ReplacementVisitor.CtElementAnnotationsReplaceListener(catchVariable)); replaceElementIfExist(catchVariable.getDefaultExpression(), new spoon.support.visitor.replace.ReplacementVisitor.CtVariableDefaultExpressionReplaceListener(catchVariable)); - replaceElementIfExist(catchVariable.getType(), new spoon.support.visitor.replace.ReplacementVisitor.CtTypedElementTypeReplaceListener(catchVariable)); replaceInListIfExist(catchVariable.getMultiTypes(), new spoon.support.visitor.replace.ReplacementVisitor.CtMultiTypedElementMultiTypesReplaceListener(catchVariable)); } diff --git a/src/test/java/spoon/test/trycatch/TryCatchTest.java b/src/test/java/spoon/test/trycatch/TryCatchTest.java index db08fda06f9..9bada64f9df 100644 --- a/src/test/java/spoon/test/trycatch/TryCatchTest.java +++ b/src/test/java/spoon/test/trycatch/TryCatchTest.java @@ -4,6 +4,7 @@ import spoon.Launcher; import spoon.SpoonModelBuilder; import spoon.reflect.code.CtCatch; +import spoon.reflect.code.CtCatchVariable; import spoon.reflect.code.CtTry; import spoon.reflect.code.CtTryWithResource; import spoon.reflect.declaration.CtClass; @@ -16,6 +17,8 @@ import spoon.test.trycatch.testclasses.Foo; import java.io.File; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -197,4 +200,53 @@ public void testCompileMultiTryCatchWithCustomExceptions() throws Exception { fail(e.getMessage()); } } + @Test + public void testTryCatchVariableGetType() throws Exception { + Factory factory = createFactory(); + CtClass clazz = factory + .Code() + .createCodeSnippetStatement( + "" + "class X {" + "public void foo() {" + + " try{}catch(RuntimeException e){System.exit(0);}" + "}" + + "};").compile(); + CtTry tryStmt = (CtTry) clazz.getElements(new TypeFilter<>(CtTry.class)).get(0); + List catchers = tryStmt.getCatchers(); + assertEquals(1, catchers.size()); + + CtCatchVariable catchVariable = catchers.get(0).getParameter(); + + assertEquals( + RuntimeException.class, + catchVariable.getType().getActualClass()); + + assertEquals(1, catchVariable.getMultiTypes().size()); + + assertEquals( + RuntimeException.class, + catchVariable.getMultiTypes().get(0).getActualClass()); + + //contract: the manipulation with catch variable type is possible + catchVariable.setType((CtTypeReference)factory.Type().createReference(IllegalArgumentException.class)); + assertEquals(IllegalArgumentException.class,catchVariable.getType().getActualClass()); + //contract setType influences multitypes + assertEquals(1, catchVariable.getMultiTypes().size()); + assertEquals(IllegalArgumentException.class, catchVariable.getMultiTypes().get(0).getActualClass()); + + catchVariable.setMultiTypes(Collections.singletonList((CtTypeReference)factory.Type().createReference(UnsupportedOperationException.class))); + assertEquals(UnsupportedOperationException.class,catchVariable.getType().getActualClass()); + //contract setType influences multitypes + assertEquals(1, catchVariable.getMultiTypes().size()); + assertEquals(UnsupportedOperationException.class, catchVariable.getMultiTypes().get(0).getActualClass()); + + catchVariable.setMultiTypes(Arrays.asList( + factory.Type().createReference(UnsupportedOperationException.class), + factory.Type().createReference(IllegalArgumentException.class) + )); + assertEquals(2, catchVariable.getMultiTypes().size()); + assertEquals(UnsupportedOperationException.class, catchVariable.getMultiTypes().get(0).getActualClass()); + assertEquals(IllegalArgumentException.class, catchVariable.getMultiTypes().get(1).getActualClass()); + + //contract setMultiTypes influences types, which contains common super class of all multi types + assertEquals(RuntimeException.class,catchVariable.getType().getActualClass()); + } }