Skip to content

Commit

Permalink
fix(position): fix position of String arg[] and lambda parameter (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
pvojtechovsky authored and tdurieux committed Jul 1, 2018
1 parent 79fe3a6 commit 88760de
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 9 deletions.
83 changes: 74 additions & 9 deletions src/main/java/spoon/support/compiler/jdt/PositionBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
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.ArrayTypeReference;
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 @@ -49,6 +50,7 @@
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 @@ -104,11 +106,11 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
//the type cast reference must be enclosed with brackets
int declarationSourceStart = sourceStart;
int declarationSourceEnd = sourceEnd;
declarationSourceStart = findPrevNonWhitespace(contents, 0, declarationSourceStart - 1);
declarationSourceStart = findPrevNonWhitespace(contents, getParentsSourceStart(), declarationSourceStart - 1);
if (contents[declarationSourceStart] != '(') {
return handlePositionProblem("Unexpected character \'" + contents[declarationSourceStart] + "\' at start of cast expression on offset: " + declarationSourceStart);
}
declarationSourceEnd = findNextNonWhitespace(contents, contents.length, declarationSourceEnd + 1);
declarationSourceEnd = findNextNonWhitespace(contents, contents.length - 1, declarationSourceEnd + 1);
if (contents[declarationSourceEnd] != ')') {
return handlePositionProblem("Unexpected character \'" + contents[declarationSourceStart] + "\' at end of cast expression on offset: " + declarationSourceEnd);
}
Expand All @@ -128,15 +130,18 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
declarationSourceStart = pos.getSourceStart();
int nrOfBrackets = getNrOfFirstCastExpressionBrackets();
while (nrOfBrackets > 0) {
declarationSourceStart = findPrevNonWhitespace(contents, 0, declarationSourceStart - 1);
declarationSourceStart = findPrevNonWhitespace(contents, getParentsSourceStart(), declarationSourceStart - 1);
if (declarationSourceStart < 0) {
return handlePositionProblem("Cannot found beginning of cast expression until offset: " + getParentsSourceStart());
}
if (contents[declarationSourceStart] != '(') {
return handlePositionProblem("Unexpected character \'" + contents[declarationSourceStart] + "\' at start of expression on offset: " + declarationSourceStart);
}
nrOfBrackets--;
}
nrOfBrackets = getNrOfCastExpressionBrackets();
while (nrOfBrackets > 0) {
declarationSourceEnd = findNextNonWhitespace(contents, contents.length, declarationSourceEnd + 1);
declarationSourceEnd = findNextNonWhitespace(contents, contents.length - 1, declarationSourceEnd + 1);
if (contents[declarationSourceEnd] != ')') {
return handlePositionProblem("Unexpected character \'" + contents[declarationSourceStart] + "\' at end of expression on offset: " + declarationSourceEnd);
}
Expand Down Expand Up @@ -190,13 +195,26 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
declarationSourceStart = bracketStart + 1;
}

int length = variableDeclaration.toString().length();
if (length > (declarationSourceEnd + 1 - declarationSourceStart)) {
declarationSourceEnd = declarationSourceStart + length - 1;
if (variableDeclaration instanceof Argument && variableDeclaration.type instanceof ArrayTypeReference) {
//handle type declarations like `String[] arg` `String arg[]` and `String []arg[]`
ArrayTypeReference arrTypeRef = (ArrayTypeReference) variableDeclaration.type;
int dimensions = arrTypeRef.dimensions();
if (dimensions > 0) {
//count number of brackets between type and variable name
int foundDimensions = getNrOfDimensions(contents, declarationSourceStart, declarationSourceEnd);
while (dimensions > foundDimensions) {
//some brackets are after the variable name
declarationSourceEnd = findNextChar(contents, contents.length, declarationSourceEnd + 1, ']');
if (declarationSourceEnd < 0) {
return handlePositionProblem("Unexpected array type declaration on offset: " + declarationSourceStart);
}
foundDimensions++;
}
}
}

if (modifiersSourceStart <= 0) {
modifiersSourceStart = findNextNonWhitespace(contents, contents.length, declarationSourceStart);
modifiersSourceStart = findNextNonWhitespace(contents, contents.length - 1, declarationSourceStart);
}
int modifiersSourceEnd;
if (variableDeclaration.type != null) {
Expand Down Expand Up @@ -354,6 +372,37 @@ SourcePosition buildPositionCtElement(CtElement e, ASTNode node) {
return cf.createSourcePosition(cu, sourceStart, sourceEnd, lineSeparatorPositions);
}

private int getParentsSourceStart() {
Iterator<ASTPair> iter = this.jdtTreeBuilder.getContextBuilder().stack.iterator();
if (iter.hasNext()) {
iter.next();
if (iter.hasNext()) {
ASTPair pair = iter.next();
SourcePosition pos = pair.element.getPosition();
if (pos.isValidPosition()) {
return pos.getSourceStart();
}
}
}
return 0;
}

private int getNrOfDimensions(char[] contents, int start, int end) {
int nrDims = 0;
while ((start = findNextNonWhitespace(contents, end, start)) >= 0) {
if (contents[start] == ']') {
nrDims++;
}
if (contents[start] == '.' && start + 2 <= end && contents[start + 1] == '.' && contents[start + 2] == '.') {
//String...arg is same like String[] arg, so it is counted as dimension too
start = start + 2;
nrDims++;
}
start++;
}
return nrDims;
}

private static final String CATCH = "catch";

void buildPosition(CtCatch catcher) {
Expand Down Expand Up @@ -438,13 +487,29 @@ private int getSourceEndOfTypeReference(char[] contents, TypeReference node, int
//the sourceEnd of reference is smaller then source of type argument of this reference
//move sourceEnd so that type argument is included in sources
//TODO handle comments correctly here. E.g. List<T /*ccc*/ >
sourceEnd = findNextNonWhitespace(contents, contents.length, getSourceEndOfTypeReference(contents, tr, tr.sourceEnd) + 1);
sourceEnd = findNextNonWhitespace(contents, contents.length - 1, getSourceEndOfTypeReference(contents, tr, tr.sourceEnd) + 1);
}
}
}
return sourceEnd;
}

/**
* @return index of first character `expectedChar`, searching forward..
* Can return 'off' if it `expectedChar`.
* Note: all kinds of java comments are understood as whitespace.
* The search must start out of comment or on the first character of the comment
*/
private int findNextChar(char[] contents, int maxOff, int off, char expectedChar) {
while ((off = findNextNonWhitespace(contents, maxOff, off)) >= 0) {
if (contents[off] == expectedChar) {
return off;
}
off++;
}
return -1;
}

/**
* @param maxOff maximum acceptable return value
* @return index of first non whitespace char, searching forward.
Expand Down
27 changes: 27 additions & 0 deletions src/test/java/spoon/test/position/PositionTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,33 @@ public void testArrayArgParameter() throws Exception {
assertEquals("String arg[]", contentAtPosition(classContent, param.getPosition()));
assertEquals("String arg[]", contentAtPosition(classContent, param.getType().getPosition()));
}
{
CtParameter<?> param = foo.getMethodsByName("m4").get(0).getParameters().get(0);
assertEquals("/*1*/ String /*2*/ arg /*3*/ [ /*4*/ ]", contentAtPosition(classContent, param.getPosition()));
assertEquals("String /*2*/ arg /*3*/ [ /*4*/ ]", contentAtPosition(classContent, param.getType().getPosition()));
}
{
CtParameter<?> param = foo.getMethodsByName("m5").get(0).getParameters().get(0);
assertEquals("/*1*/ String /*2*/ arg /*3*/ [ /*4 []*/ ] /* 5 */[][]/**/ []", contentAtPosition(classContent, param.getPosition()));
assertEquals("String /*2*/ arg /*3*/ [ /*4 []*/ ] /* 5 */[][]/**/ []", contentAtPosition(classContent, param.getType().getPosition()));
}
{
CtParameter<?> param = foo.getMethodsByName("m6").get(0).getParameters().get(0);
assertEquals("String[]//[]\n" +
" p[]", contentAtPosition(classContent, param.getPosition()));
assertEquals("String[]//[]\n" +
" p[]", contentAtPosition(classContent, param.getType().getPosition()));
}
{
CtParameter<?> param = foo.getMethodsByName("m7").get(0).getParameters().get(0);
assertEquals("String...arg", contentAtPosition(classContent, param.getPosition()));
assertEquals("String...", contentAtPosition(classContent, param.getType().getPosition()));
}
{
CtParameter<?> param = foo.getMethodsByName("m8").get(0).getParameters().get(0);
assertEquals("String[]...arg", contentAtPosition(classContent, param.getPosition()));
assertEquals("String[]...", contentAtPosition(classContent, param.getType().getPosition()));
}
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,10 @@ public interface ArrayArgParameter {
void m1(String[] arg);
void m2(String []arg);
void m3(String arg[]);
void m4(/*1*/ String /*2*/ arg /*3*/ [ /*4*/ ] /* 5 */);
void m5(/*1*/ String /*2*/ arg /*3*/ [ /*4 []*/ ] /* 5 */[][]/**/ []);
void m6(String[]//[]
p[]);
void m7(String...arg);
void m8(String[]...arg);
}
10 changes: 10 additions & 0 deletions src/test/java/spoon/test/position/testclasses/FooLambda.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package spoon.test.position.testclasses;

import java.util.function.Predicate;

public class FooLambda {

public static Predicate<Integer> m() {
return i -> i.compareTo(7) > 0;
}
}

0 comments on commit 88760de

Please sign in to comment.