/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.jdt.core.groovy.tests.locations;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import groovy.lang.GroovyClassLoader;
import org.codehaus.groovy.ast.ASTNode;
import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
import org.codehaus.groovy.ast.ClassNode;
import org.codehaus.groovy.ast.ImportNode;
import org.codehaus.groovy.ast.ModuleNode;
import org.codehaus.groovy.ast.expr.BinaryExpression;
import org.codehaus.groovy.ast.expr.CastExpression;
import org.codehaus.groovy.ast.expr.MapEntryExpression;
import org.codehaus.groovy.ast.expr.MapExpression;
import org.codehaus.groovy.ast.stmt.AssertStatement;
import org.codehaus.groovy.control.CompilerConfiguration;
import org.codehaus.groovy.control.ErrorCollector;
import org.codehaus.groovy.control.SourceUnit;
import org.eclipse.jdt.core.groovy.tests.builder.BuilderTestSuite;
import org.eclipse.jdt.groovy.core.util.GroovyUtils;
import org.junit.Ignore;
import org.junit.Test;
/**
* Test the source locations of ASTNodes to ensure they are correct, especially
* look into the changes that we force into them.
*/
public final class ASTNodeSourceLocationsTests extends BuilderTestSuite {
private static class StartAndEnd {
final int start;
final int end;
public StartAndEnd(int start, int end) {
this.start = start;
this.end = end;
}
public StartAndEnd(ASTNode node) {
this(node.getStart(), node.getEnd());
}
boolean isOK(ASTNode node) {
return node.getStart() == start && node.getEnd() == end;
}
@Override
public String toString() {
return "[start=" + start + ", end=" + end + "]";
}
}
private static abstract class AbstractSLocTester extends ClassCodeVisitorSupport {
List<ASTNode> allCollectedNodes = new ArrayList<ASTNode>();
void doTest(ModuleNode module, StartAndEnd...sae) {
for (ClassNode c : (Iterable<ClassNode>) module.getClasses()) {
this.visitClass(c);
}
assertStartAndEnds(sae);
}
void assertStartAndEnds(StartAndEnd...sae) {
assertEquals("Wrong number expressions found", sae.length, allCollectedNodes.size());
ASTNode[] bexprs = allCollectedNodes.toArray(new ASTNode[0]);
List<Integer> problemIndices = new ArrayList<Integer>();
for (int i = 0; i < bexprs.length; i++) {
if (! sae[i].isOK(bexprs[i])) {
problemIndices.add(i);
}
}
if (problemIndices.size() > 0) {
StringBuilder sb = new StringBuilder();
for (Integer integer : problemIndices) {
int val = integer.intValue();
sb.append("Expected slocs at " + sae[val] + " for expression " + bexprs[val] +
"but instead found: " + new StartAndEnd(bexprs[val]) + "\n");
}
fail(sb.toString());
}
}
}
private static class ImportStatementSLocTester extends AbstractSLocTester {
@Override
public void visitImports(ModuleNode module) {
for (ImportNode node : GroovyUtils.getAllImportNodes(module)) {
allCollectedNodes.add(node);
}
}
}
private static class BinaryExpressionSLocTester extends AbstractSLocTester {
@Override
public void visitBinaryExpression(BinaryExpression expression) {
super.visitBinaryExpression(expression);
allCollectedNodes.add(expression);
}
}
private static class MapExpressionSLocTester extends AbstractSLocTester {
@Override
public void visitMapExpression(MapExpression expression) {
super.visitMapExpression(expression);
allCollectedNodes.add(expression);
}
}
private static class MapEntryExpressionSLocTester extends AbstractSLocTester {
@Override
public void visitMapEntryExpression(MapEntryExpression expression) {
super.visitMapEntryExpression(expression);
allCollectedNodes.add(expression);
}
}
private static class CastExpressionSLocTester extends AbstractSLocTester {
@Override
public void visitCastExpression(CastExpression expression) {
super.visitCastExpression(expression);
allCollectedNodes.add(expression);
}
}
private static class AssertStatementSLocTester extends AbstractSLocTester {
@Override
public void visitAssertStatement(AssertStatement statement) {
super.visitAssertStatement(statement);
allCollectedNodes.add(statement);
}
}
private static void checkBinaryExprSLocs(String contents, AbstractSLocTester tester, String... exprStrings) throws Exception {
StartAndEnd[] points = convertToPoints(contents, exprStrings);
ModuleNode module = createModuleNodeFor(contents);
tester.doTest(module, points);
}
private static void checkBinaryExprSLocsReverse(String contents, String...exprStrings) throws Exception {
StartAndEnd[] points = convertToPoints(contents, exprStrings);
List<StartAndEnd> list = Arrays.asList(points);
Collections.reverse(list);
points = list.toArray(points);
ModuleNode module = createModuleNodeFor(contents);
BinaryExpressionSLocTester tester = new BinaryExpressionSLocTester();
tester.doTest(module, points);
}
private static StartAndEnd[] convertToPoints(String contents, String[] exprStrings) {
StartAndEnd[] points = new StartAndEnd[exprStrings.length];
int prevEnd = 0;
int prevStart = 0;
for (int i = 0; i < exprStrings.length; i++) {
int start = contents.indexOf(exprStrings[i], prevEnd);
if (start == -1) {
// now try prevStart
start = contents.indexOf(exprStrings[i], prevStart);
if (start == -1) {
// now try from the beginning
start = contents.indexOf(exprStrings[i]);
if (start == -1) {
fail("Could not find exprString");
}
}
}
int end = start + exprStrings[i].length();
points[i] = new StartAndEnd(start, end);
prevStart = start;
prevEnd = end + 1;
}
return points;
}
private static ModuleNode createModuleNodeFor(String contents) throws Exception {
SourceUnit sourceUnit = new SourceUnit("Foo", contents, new CompilerConfiguration(), new GroovyClassLoader(), new ErrorCollector(new CompilerConfiguration()));
sourceUnit.parse();
sourceUnit.completePhase();
sourceUnit.convert();
return sourceUnit.getAST();
}
//--------------------------------------------------------------------------
@Test
public void testBinaryExpr1() throws Exception {
checkBinaryExprSLocs(
"def map = [:]\n" +
"map = [:]",
new BinaryExpressionSLocTester(),
"def map = [:]", "map = [:]");
}
@Test
public void testBinaryExpr2() throws Exception {
checkBinaryExprSLocs(
"def foo = [1, 2] as Set\n" +
"foo == [1, 2] as Set",
new BinaryExpressionSLocTester(),
"def foo = [1, 2] as Set",
"foo == [1, 2] as Set");
}
@Test
public void testBinaryExpr3() throws Exception {
checkBinaryExprSLocs(
"(foo == [1, 2] as Set)",
new BinaryExpressionSLocTester(),
"(foo == [1, 2] as Set)");
}
@Test
public void testBinaryExpr4() throws Exception {
checkBinaryExprSLocs(
"((foo == [1, 2] as Set))",
new BinaryExpressionSLocTester(),
"((foo == [1, 2] as Set))");
}
@Test
public void testBinaryExpr5() throws Exception {
checkBinaryExprSLocsReverse(
"[:] + [:] + [:]",
"[:] + [:] + [:]",
"[:] + [:]");
}
@Test
public void testBinaryExpr6() throws Exception {
checkBinaryExprSLocsReverse(
"[:] << [:] + [:]",
"[:] << [:] + [:]",
"[:] + [:]");
}
@Test // Not right!!! ending whitespace is included, but shouldm't be.
public void testBinaryExpr7() throws Exception {
checkBinaryExprSLocs(
" a = b ",
new BinaryExpressionSLocTester(),
"a = b ");
}
@Test
public void testMapExpression1() throws Exception {
checkBinaryExprSLocs(
"[:]",
new MapExpressionSLocTester(),
"[:]");
}
@Test
public void testMapExpression2() throws Exception {
checkBinaryExprSLocs(
"[ : ]",
new MapExpressionSLocTester(),
"[ : ]");
}
@Test
public void testMapExpression3() throws Exception {
checkBinaryExprSLocs(
"def x = [:]",
new MapExpressionSLocTester(),
"[:]");
}
@Test @Ignore("fails because we are not smart about how we extend slocs for empty map expressions")
public void testMapExpression4() throws Exception {
checkBinaryExprSLocs(
"def x = [ : ]",
new MapExpressionSLocTester(),
"[ : ]");
}
@Test
public void testMapEntryExpression1() throws Exception {
checkBinaryExprSLocs(
"[a:b]",
new MapEntryExpressionSLocTester(),
"a:b");
}
@Test
public void testMapEntryExpression2() throws Exception {
checkBinaryExprSLocs(
"[a : b]",
new MapEntryExpressionSLocTester(),
"a : b");
}
@Test // has extra whitespace at end, but should not
public void testMapEntryExpression3() throws Exception {
checkBinaryExprSLocs(
"[a : b ]",
new MapEntryExpressionSLocTester(),
"a : b");
}
@Test
public void testMapEntryExpression4() throws Exception {
checkBinaryExprSLocs(
"[a : b, c : d]",
new MapEntryExpressionSLocTester(),
"a : b", "c : d");
}
@Test
public void testMapEntryExpression5() throws Exception {
checkBinaryExprSLocs(
"def x = [a : b, c : d]",
new MapEntryExpressionSLocTester(),
"a : b", "c : d");
}
@Test
public void testMapEntryExpression6() throws Exception {
checkBinaryExprSLocs(
"def x = [a : b] << [ c : d]",
new MapEntryExpressionSLocTester(),
"a : b", "c : d");
}
@Test
public void testMapEntryExpression7() throws Exception {
checkBinaryExprSLocs(
"def x = [a : b, e : [ c : d]]",
new MapEntryExpressionSLocTester(),
"a : b", "c : d", "e : [ c : d]");
}
@Test
public void testCastExpression1() throws Exception {
checkBinaryExprSLocs(
"foo as Set",
new CastExpressionSLocTester(),
"foo as Set");
}
@Test
public void testCastExpression2() throws Exception {
checkBinaryExprSLocs(
"def x = foo as Set",
new CastExpressionSLocTester(),
"foo as Set");
}
@Test
public void testCastExpression3() throws Exception {
checkBinaryExprSLocs(
"def x = foo as Set ",
new CastExpressionSLocTester(),
"foo as Set");
}
@Test
public void testImportStatement1() throws Exception {
checkBinaryExprSLocs(
"import javax.swing.*\n" +
"import javax.swing.JFrame\n" +
"import javax.applet.*;\n" +
"import javax.applet.Applet;\n",
new ImportStatementSLocTester(),
"import javax.swing.*",
"import javax.swing.JFrame",
"import javax.applet.*",
"import javax.applet.Applet");
}
@Test // GRECLIPSE-1270
public void testAssertStatement1() throws Exception {
checkBinaryExprSLocs(
"def meth() {\n then:\n assert x == 9\n}",
new AssertStatementSLocTester(),
"assert x == 9");
}
}