Skip to content

Commit

Permalink
fix: position of CtCatch and CtCatchVariable
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky committed Jun 29, 2018
1 parent 81076ce commit d6b01a3
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 31 deletions.
9 changes: 9 additions & 0 deletions src/main/java/spoon/support/compiler/jdt/ContextBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,15 @@ void exit(ASTNode node) {
}
}

<T extends CtElement> T getParentContextOfType(Class<T> clazz) {
for (ASTPair pair : stack) {
if (clazz.isInstance(pair.element)) {
return (T) pair.element;
}
}
return null;
}

@SuppressWarnings("unchecked")
<T> CtLocalVariable<T> getLocalVariableDeclaration(final String name) {
final Class<CtLocalVariable<T>> clazz = (Class<CtLocalVariable<T>>)
Expand Down
20 changes: 18 additions & 2 deletions src/main/java/spoon/support/compiler/jdt/ParentExiter.java
Original file line number Diff line number Diff line change
Expand Up @@ -845,8 +845,11 @@ public void visitCtThrow(CtThrow throwStatement) {
public void visitCtTry(CtTry tryBlock) {
if (child instanceof CtBlock) {
final CtBlock<?> childBlock = (CtBlock<?>) this.child;
if (tryBlock.getCatchers().size() > 0 && tryBlock.getCatchers().get(tryBlock.getCatchers().size() - 1).getBody() == null) {
tryBlock.getCatchers().get(tryBlock.getCatchers().size() - 1).setBody(childBlock);
CtCatch lastCatcher = getLastCatcher(tryBlock);
if (lastCatcher != null && lastCatcher.getBody() == null) {
lastCatcher.setBody(childBlock);
//we have finally all the information needed to build full position of CtCatch element
jdtTreeBuilder.getPositionBuilder().buildPosition(lastCatcher);
} else if (tryBlock.getBody() != null && tryBlock.getFinalizer() == null) {
tryBlock.setFinalizer(childBlock);
} else {
Expand All @@ -860,6 +863,19 @@ public void visitCtTry(CtTry tryBlock) {
super.visitCtTry(tryBlock);
}

/**
* @param tryBlock
* @return last CtCatch of `tryBlock` or null
*/
private CtCatch getLastCatcher(CtTry tryBlock) {
List<CtCatch> catchers = tryBlock.getCatchers();
int nrCatchers = catchers.size();
if (nrCatchers > 0) {
return catchers.get(nrCatchers - 1);
}
return null;
}

@Override
public void visitCtTryWithResource(CtTryWithResource tryWithResource) {
if (child instanceof CtLocalVariable) {
Expand Down
99 changes: 70 additions & 29 deletions src/main/java/spoon/support/compiler/jdt/PositionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import org.eclipse.jdt.internal.compiler.ast.AllocationExpression;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.AnnotationMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Initializer;
Expand All @@ -33,9 +32,11 @@
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import spoon.SpoonException;
import spoon.reflect.code.CtCatch;
import spoon.reflect.code.CtCatchVariable;
import spoon.reflect.code.CtExpression;
import spoon.reflect.code.CtStatementList;
import spoon.reflect.code.CtTry;
import spoon.reflect.cu.CompilationUnit;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.cu.position.DeclarationSourcePosition;
Expand All @@ -47,7 +48,6 @@
import spoon.support.compiler.jdt.ContextBuilder.CastInfo;
import spoon.support.reflect.CtExtendedModifier;

import java.util.Iterator;
import java.util.List;
import java.util.Set;

Expand Down Expand Up @@ -157,17 +157,42 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
int declarationSourceStart = variableDeclaration.declarationSourceStart;
int declarationSourceEnd = variableDeclaration.declarationSourceEnd;

if (e instanceof CtCatch) {
/* compiler delivers wrong declarationSourceStart in case like: */
//... catch/*2*/ ( /*3*/ final @Deprecated /*4*/ ClassCastException /*5*/ e /*6*/) /*7*/ {
/*
* the declarationSourceStart should be after the '(', but sometime it is before
* So we have to compute correct offset here
*/
CtTry tryStatement = this.jdtTreeBuilder.getContextBuilder().getParentContextOfType(CtTry.class);
int endOfTry = tryStatement.getPosition().getSourceEnd();
//offset of the bracket before catch
int lastBracket = getEndOfLastTryBlock(tryStatement, 0);
int catchStart = findNextNonWhitespace(contents, endOfTry, lastBracket + 1);
if (CATCH.equals(new String(contents, catchStart, CATCH.length())) == false) {
throw new SpoonException("Unexpected beginning of catch statement on offset: " + catchStart);
}
int bracketStart = findNextNonWhitespace(contents, endOfTry, catchStart + CATCH.length());
if (bracketStart < 0) {
throw new SpoonException("Unexpected end of file instead of \'(\' after catch statement on offset: " + catchStart);
}
if (contents[bracketStart] != '(') {
throw new SpoonException("Unexpected character " + contents[bracketStart] + " instead of \'(\' after catch statement on offset: " + bracketStart);
}
declarationSourceStart = bracketStart + 1;
}

int length = variableDeclaration.toString().length();
if (length > (declarationSourceEnd - declarationSourceStart)) {
if (length > (declarationSourceEnd + 1 - declarationSourceStart)) {
declarationSourceEnd = declarationSourceStart + length - 1;
}

if (modifiersSourceStart <= 0) {
modifiersSourceStart = declarationSourceStart;
modifiersSourceStart = findNextNonWhitespace(contents, contents.length, declarationSourceStart);
}
int modifiersSourceEnd;
if (variableDeclaration.type != null) {
modifiersSourceEnd = variableDeclaration.type.sourceStart() - 2;
modifiersSourceEnd = findPrevNonWhitespace(contents, declarationSourceStart, variableDeclaration.type.sourceStart() - 1);
} else if (variableDeclaration instanceof Initializer) {
modifiersSourceEnd = ((Initializer) variableDeclaration).block.sourceStart - 1;
} else {
Expand Down Expand Up @@ -296,30 +321,9 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
lineSeparatorPositions);
}
} else if (e instanceof CtCatchVariable) {
Iterator<ASTPair> iterator = this.jdtTreeBuilder.getContextBuilder().stack.iterator();
ASTPair catchNode = iterator.next();
while (!(catchNode.node instanceof Argument)) {
catchNode = iterator.next();
}
DeclarationSourcePosition argumentPosition = (DeclarationSourcePosition) buildPositionCtElement(e, catchNode.node);

int variableNameStart = findNextNonWhitespace(contents, argumentPosition.getSourceEnd(), sourceEnd + 1);
int variableNameEnd = argumentPosition.getSourceEnd();

int modifierStart = sourceStart;
int modifierEnd = sourceStart - 1;
if (!getModifiers(((Argument) catchNode.node).modifiers, false, false).isEmpty()) {
modifierStart = argumentPosition.getModifierSourceStart();
modifierEnd = argumentPosition.getModifierSourceEnd();

sourceStart = modifierStart;
}
sourceEnd = argumentPosition.getSourceEnd();
return cf.createDeclarationSourcePosition(cu,
variableNameStart, variableNameEnd,
modifierStart, modifierEnd,
sourceStart, sourceEnd,
lineSeparatorPositions);
CtCatch catcher = this.jdtTreeBuilder.getContextBuilder().getParentContextOfType(CtCatch.class);
//the CtCatch has the position of CTCatchVariable now, so take it
return (DeclarationSourcePosition) catcher.getPosition();
} else if (node instanceof TypeReference) {
sourceEnd = getSourceEndOfTypeReference(contents, (TypeReference) node, sourceEnd);
} else if (node instanceof AllocationExpression) {
Expand All @@ -337,6 +341,43 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
return cf.createSourcePosition(cu, sourceStart, sourceEnd, lineSeparatorPositions);
}

private static final String CATCH = "catch";

void buildPosition(CtCatch catcher) {
CompilationResult cr = this.jdtTreeBuilder.getContextBuilder().compilationunitdeclaration.compilationResult;
int[] lineSeparatorPositions = cr.lineSeparatorPositions;
char[] contents = cr.compilationUnit.getContents();

CtTry tryElement = catcher.getParent(CtTry.class);
//offset after last bracket before catch
int declarationStart = getEndOfLastTryBlock(tryElement, 1) + 1;
DeclarationSourcePosition oldCatcherPos = (DeclarationSourcePosition) catcher.getPosition();
int bodyStart = catcher.getBody().getPosition().getSourceStart();
int bodyEnd = catcher.getBody().getPosition().getSourceEnd();
catcher.setPosition(catcher.getFactory().Core().createBodyHolderSourcePosition(
tryElement.getPosition().getCompilationUnit(),
oldCatcherPos.getNameStart(), oldCatcherPos.getNameEnd(),
oldCatcherPos.getModifierSourceStart(), oldCatcherPos.getModifierSourceEnd(),
declarationStart, bodyEnd,
bodyStart, bodyEnd,
lineSeparatorPositions));
}

/**
* @param tryElement
* @param negIdx 0 - last block, 1 - one before last block, ...
* @return
*/
private int getEndOfLastTryBlock(CtTry tryElement, int negIdx) {
//offset where we can start to search for catch
int endOfLastBlock = tryElement.getBody().getPosition().getSourceEnd();
if (tryElement.getCatchers().size() > negIdx) {
CtCatch prevCatcher = tryElement.getCatchers().get(tryElement.getCatchers().size() - 1 - negIdx);
endOfLastBlock = prevCatcher.getPosition().getSourceEnd();
}
return endOfLastBlock;
}

private int getNrOfFirstCastExpressionBrackets() {
return this.jdtTreeBuilder.getContextBuilder().casts.get(0).nrOfBrackets;
}
Expand Down

0 comments on commit d6b01a3

Please sign in to comment.