7073631: (javac) javac parser improvements for error position reporting
Summary: JavacParser improvements for NetBeans, improved by LangTools.
Reviewed-by: mcimadamore, jjg
Contributed-by: jan.lahoda@oracle.com
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Thu Sep 01 09:14:25 2011 -0700
@@ -27,15 +27,15 @@
import java.util.*;
+import com.sun.tools.javac.code.*;
import com.sun.tools.javac.tree.*;
-import com.sun.tools.javac.code.*;
+import com.sun.tools.javac.tree.JCTree.*;
import com.sun.tools.javac.util.*;
import com.sun.tools.javac.util.JCDiagnostic.DiagnosticFlag;
+import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
import com.sun.tools.javac.util.List;
-import static com.sun.tools.javac.util.ListBuffer.lb;
-import com.sun.tools.javac.tree.JCTree.*;
-
+import static com.sun.tools.javac.util.ListBuffer.lb;
import static com.sun.tools.javac.parser.Token.*;
/** The parser maps a token sequence into an abstract syntax
@@ -254,26 +254,44 @@
}
private JCErroneous syntaxError(int pos, String key, Token... args) {
- return syntaxError(pos, null, key, args);
+ return syntaxError(pos, List.<JCTree>nil(), key, args);
}
private JCErroneous syntaxError(int pos, List<JCTree> errs, String key, Token... args) {
setErrorEndPos(pos);
- reportSyntaxError(pos, key, (Object[])args);
- return toP(F.at(pos).Erroneous(errs));
+ JCErroneous err = F.at(pos).Erroneous(errs);
+ reportSyntaxError(err, key, (Object[])args);
+ if (errs != null) {
+ JCTree last = errs.last();
+ if (last != null)
+ storeEnd(last, pos);
+ }
+ return toP(err);
}
private int errorPos = Position.NOPOS;
+
/**
- * Report a syntax error at given position using the given
- * argument unless one was already reported at the same position.
+ * Report a syntax using the given the position parameter and arguments,
+ * unless one was already reported at the same position.
*/
private void reportSyntaxError(int pos, String key, Object... args) {
+ JCDiagnostic.DiagnosticPosition diag = new JCDiagnostic.SimpleDiagnosticPosition(pos);
+ reportSyntaxError(diag, key, args);
+ }
+
+ /**
+ * Report a syntax error using the given DiagnosticPosition object and
+ * arguments, unless one was already reported at the same position.
+ */
+ private void reportSyntaxError(JCDiagnostic.DiagnosticPosition diagPos, String key, Object... args) {
+ int pos = diagPos.getPreferredPosition();
if (pos > S.errPos() || pos == Position.NOPOS) {
- if (S.token() == EOF)
- error(pos, "premature.eof");
- else
- error(pos, key, args);
+ if (S.token() == EOF) {
+ error(diagPos, "premature.eof");
+ } else {
+ error(diagPos, key, args);
+ }
}
S.errPos(pos);
if (S.pos() == errorPos)
@@ -311,7 +329,7 @@
/** Report an illegal start of expression/type error at given position.
*/
JCExpression illegal(int pos) {
- setErrorEndPos(S.pos());
+ setErrorEndPos(pos);
if ((mode & EXPR) != 0)
return syntaxError(pos, "illegal.start.of.expr");
else
@@ -340,7 +358,7 @@
* indexed by the tree nodes they refer to.
* defined only if option flag keepDocComment is set.
*/
- Map<JCTree, String> docComments;
+ private final Map<JCTree, String> docComments;
/** Make an entry into docComments hashtable,
* provided flag keepDocComments is set and given doc comment is non-null.
@@ -462,6 +480,10 @@
return t;
}
+ JCExpression literal(Name prefix) {
+ return literal(prefix, S.pos());
+ }
+
/**
* Literal =
* INTLITERAL
@@ -474,8 +496,7 @@
* | FALSE
* | NULL
*/
- JCExpression literal(Name prefix) {
- int pos = S.pos();
+ JCExpression literal(Name prefix, int pos) {
JCExpression t = errorTree;
switch (S.token()) {
case INTLITERAL:
@@ -869,7 +890,7 @@
(S.token() == INTLITERAL || S.token() == LONGLITERAL) &&
S.radix() == 10) {
mode = EXPR;
- t = literal(names.hyphen);
+ t = literal(names.hyphen, pos);
} else {
t = term3();
return F.at(pos).Unary(unoptag(token), t);
@@ -1267,15 +1288,17 @@
case GTGT:
S.token(GT);
break;
+ case GT:
+ S.nextToken();
+ break;
default:
- accept(GT);
+ args.append(syntaxError(S.pos(), "expected", GT));
break;
}
return args.toList();
}
} else {
- syntaxError(S.pos(), "expected", LT);
- return List.nil();
+ return List.<JCExpression>of(syntaxError(S.pos(), "expected", LT));
}
}
@@ -1300,12 +1323,12 @@
return F.at(pos).Wildcard(t, bound);
} else if (S.token() == IDENTIFIER) {
//error recovery
- reportSyntaxError(S.prevEndPos(), "expected3",
- GT, EXTENDS, SUPER);
TypeBoundKind t = F.at(Position.NOPOS).TypeBoundKind(BoundKind.UNBOUND);
JCExpression wc = toP(F.at(pos).Wildcard(t, null));
JCIdent id = toP(F.at(S.pos()).Ident(ident()));
- return F.at(pos).Erroneous(List.<JCTree>of(wc, id));
+ JCErroneous err = F.at(pos).Erroneous(List.<JCTree>of(wc, id));
+ reportSyntaxError(err, "expected3", GT, EXTENDS, SUPER);
+ return err;
} else {
TypeBoundKind t = toP(F.at(pos).TypeBoundKind(BoundKind.UNBOUND));
return toP(F.at(pos).Wildcard(t, null));
@@ -1391,7 +1414,7 @@
while (S.token() == DOT) {
if (diamondFound) {
//cannot select after a diamond
- illegal(S.pos());
+ illegal();
}
int pos = S.pos();
S.nextToken();
@@ -1419,15 +1442,16 @@
pos = typeArgs.head.pos;
}
setErrorEndPos(S.prevEndPos());
- reportSyntaxError(pos, "cannot.create.array.with.type.arguments");
- return toP(F.at(newpos).Erroneous(typeArgs.prepend(e)));
+ JCErroneous err = F.at(pos).Erroneous(typeArgs.prepend(e));
+ reportSyntaxError(err, "cannot.create.array.with.type.arguments");
+ return toP(err);
}
return e;
} else if (S.token() == LPAREN) {
return classCreatorRest(newpos, null, typeArgs, t);
} else {
- reportSyntaxError(S.pos(), "expected2",
- LPAREN, LBRACKET);
+ setErrorEndPos(S.pos());
+ reportSyntaxError(S.pos(), "expected2", LPAREN, LBRACKET);
t = toP(F.at(newpos).NewClass(null, typeArgs, t, List.<JCExpression>nil(), null));
return toP(F.at(newpos).Erroneous(List.<JCTree>of(t)));
}
@@ -1457,7 +1481,8 @@
if (S.token() == LBRACE) {
return arrayInitializer(newpos, elemtype);
} else {
- return syntaxError(S.pos(), "array.dimension.missing");
+ JCExpression t = toP(F.at(newpos).NewArray(elemtype, List.<JCExpression>nil(), null));
+ return syntaxError(S.pos(), List.<JCTree>of(t), "array.dimension.missing");
}
} else {
ListBuffer<JCExpression> dims = new ListBuffer<JCExpression>();
@@ -1843,7 +1868,7 @@
/** CatchClause = CATCH "(" FormalParameter ")" Block
*/
- JCCatch catchClause() {
+ protected JCCatch catchClause() {
int pos = S.pos();
accept(CATCH);
accept(LPAREN);
@@ -1973,7 +1998,7 @@
JCModifiers modifiersOpt() {
return modifiersOpt(null);
}
- JCModifiers modifiersOpt(JCModifiers partial) {
+ protected JCModifiers modifiersOpt(JCModifiers partial) {
long flags;
ListBuffer<JCAnnotation> annotations = new ListBuffer<JCAnnotation>();
int pos;
@@ -2006,6 +2031,7 @@
case SYNCHRONIZED: flag = Flags.SYNCHRONIZED; break;
case STRICTFP : flag = Flags.STRICTFP; break;
case MONKEYS_AT : flag = Flags.ANNOTATION; break;
+ case ERROR : flag = 0; S.nextToken(); break;
default: break loop;
}
if ((flags & flag) != 0) error(S.pos(), "repeated.modifier");
@@ -2219,9 +2245,12 @@
/** Resource = VariableModifiersOpt Type VariableDeclaratorId = Expression
*/
- JCTree resource() {
- return variableDeclaratorRest(S.pos(), optFinal(Flags.FINAL),
- parseType(), ident(), true, null);
+ protected JCTree resource() {
+ JCModifiers optFinal = optFinal(Flags.FINAL);
+ JCExpression type = parseType();
+ int pos = S.pos();
+ Name ident = ident();
+ return variableDeclaratorRest(pos, optFinal, type, ident, true, null);
}
/** CompilationUnit = [ { "@" Annotation } PACKAGE Qualident ";"] {ImportDeclaration} {TypeDeclaration}
@@ -2568,7 +2597,7 @@
* | ModifiersOpt Type Ident
* ( ConstantDeclaratorsRest | InterfaceMethodDeclaratorRest ";" )
*/
- List<JCTree> classOrInterfaceBodyDeclaration(Name className, boolean isInterface) {
+ protected List<JCTree> classOrInterfaceBodyDeclaration(Name className, boolean isInterface) {
if (S.token() == SEMI) {
S.nextToken();
return List.<JCTree>nil();
@@ -2770,7 +2799,7 @@
/** FormalParameter = { FINAL | '@' Annotation } Type VariableDeclaratorId
* LastFormalParameter = { FINAL | '@' Annotation } Type '...' Ident | FormalParameter
*/
- JCVariableDecl formalParameter() {
+ protected JCVariableDecl formalParameter() {
JCModifiers mods = optFinal(Flags.PARAMETER);
JCExpression type = parseType();
if (S.token() == ELLIPSIS) {
@@ -2788,6 +2817,10 @@
log.error(DiagnosticFlag.SYNTAX, pos, key, args);
}
+ void error(DiagnosticPosition pos, String key, Object ... args) {
+ log.error(DiagnosticFlag.SYNTAX, pos, key, args);
+ }
+
void warning(int pos, String key, Object ... args) {
log.warning(pos, key, args);
}
@@ -2807,8 +2840,9 @@
case JCTree.ERRONEOUS:
return t;
default:
- error(t.pos, "not.stmt");
- return F.at(t.pos).Erroneous(List.<JCTree>of(t));
+ JCExpression ret = F.at(t.pos).Erroneous(List.<JCTree>of(t));
+ error(ret, "not.stmt");
+ return ret;
}
}
--- a/langtools/src/share/classes/com/sun/tools/javac/parser/Scanner.java Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/parser/Scanner.java Thu Sep 01 09:14:25 2011 -0700
@@ -982,8 +982,16 @@
}
/** Sets the current token.
+ * This method is primarily used to update the token stream when the
+ * parser is handling the end of nested type arguments such as
+ * {@code List<List<String>>} and needs to disambiguate between
+ * repeated use of ">" and relation operators such as ">>" and ">>>". Noting
+ * that this does not handle arbitrary tokens containing Unicode escape
+ * sequences.
*/
public void token(Token token) {
+ pos += this.token.name.length() - token.name.length();
+ prevEndPos = pos;
this.token = token;
}
--- a/langtools/src/share/classes/com/sun/tools/javac/util/AbstractLog.java Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/src/share/classes/com/sun/tools/javac/util/AbstractLog.java Thu Sep 01 09:14:25 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -96,6 +96,19 @@
/** Report an error, unless another error was already reported at same
* source position.
+ * @param flag A flag to set on the diagnostic
+ * @param pos The source position at which to report the error.
+ * @param key The key for the localized error message.
+ * @param args Fields of the error message.
+ */
+ public void error(DiagnosticFlag flag, DiagnosticPosition pos, String key, Object ... args) {
+ JCDiagnostic d = diags.error(source, pos, key, args);
+ d.setFlag(flag);
+ report(d);
+ }
+
+ /** Report an error, unless another error was already reported at same
+ * source position.
* @param pos The source position at which to report the error.
* @param key The key for the localized error message.
* @param args Fields of the error message.
--- a/langtools/test/tools/javac/TryWithResources/BadTwr.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/BadTwr.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,5 +1,5 @@
-BadTwr.java:13:39: compiler.err.already.defined: r1, main(java.lang.String...)
-BadTwr.java:18:13: compiler.err.already.defined: args, main(java.lang.String...)
+BadTwr.java:13:46: compiler.err.already.defined: r1, main(java.lang.String...)
+BadTwr.java:18:20: compiler.err.already.defined: args, main(java.lang.String...)
BadTwr.java:21:13: compiler.err.cant.assign.val.to.final.var: thatsIt
-BadTwr.java:26:17: compiler.err.already.defined: name, main(java.lang.String...)
+BadTwr.java:26:24: compiler.err.already.defined: name, main(java.lang.String...)
4 errors
--- a/langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/DuplicateResourceDecl.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,2 +1,2 @@
-DuplicateResourceDecl.java:12:45: compiler.err.already.defined: c, main(java.lang.String[])
+DuplicateResourceDecl.java:12:56: compiler.err.already.defined: c, main(java.lang.String[])
1 error
--- a/langtools/test/tools/javac/TryWithResources/ResourceInterface.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/ResourceInterface.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,2 +1,2 @@
-ResourceInterface.java:38:13: compiler.err.unreported.exception.implicit.close: ResourceInterface.E1, r2
+ResourceInterface.java:38:23: compiler.err.unreported.exception.implicit.close: ResourceInterface.E1, r2
1 error
--- a/langtools/test/tools/javac/TryWithResources/TwrFlow.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/TwrFlow.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,3 +1,3 @@
TwrFlow.java:14:11: compiler.err.except.never.thrown.in.try: java.io.IOException
-TwrFlow.java:12:13: compiler.err.unreported.exception.implicit.close: CustomCloseException, twrFlow
+TwrFlow.java:12:21: compiler.err.unreported.exception.implicit.close: CustomCloseException, twrFlow
2 errors
--- a/langtools/test/tools/javac/TryWithResources/TwrLint.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/TwrLint.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,3 +1,3 @@
TwrLint.java:14:15: compiler.warn.try.explicit.close.call
-TwrLint.java:13:13: compiler.warn.try.resource.not.referenced: r3
+TwrLint.java:13:21: compiler.warn.try.resource.not.referenced: r3
2 warnings
--- a/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/TryWithResources/TwrOnNonResource.out Thu Sep 01 09:14:25 2011 -0700
@@ -1,4 +1,4 @@
-TwrOnNonResource.java:12:13: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
-TwrOnNonResource.java:15:13: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
-TwrOnNonResource.java:18:13: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:12:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:15:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
+TwrOnNonResource.java:18:30: compiler.err.prob.found.req: (compiler.misc.try.not.applicable.to.type), TwrOnNonResource, java.lang.AutoCloseable
3 errors
--- a/langtools/test/tools/javac/diags/examples/EmptyCharLiteral.java Wed Aug 31 15:39:00 2011 -0700
+++ b/langtools/test/tools/javac/diags/examples/EmptyCharLiteral.java Thu Sep 01 09:14:25 2011 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2010, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -23,7 +23,6 @@
// key: compiler.err.empty.char.lit
// key: compiler.err.unclosed.char.lit
-// key: compiler.err.expected
// key: compiler.err.premature.eof
class X {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/parser/netbeans/JavacParserTest.java Thu Sep 01 09:14:25 2011 -0700
@@ -0,0 +1,716 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7073631
+ * @summary tests error and diagnostics positions
+ * @author jan.lahoda@oracle.com
+ */
+
+import com.sun.source.tree.BinaryTree;
+import com.sun.source.tree.BlockTree;
+import com.sun.source.tree.ClassTree;
+import com.sun.source.tree.CompilationUnitTree;
+import com.sun.source.tree.ExpressionStatementTree;
+import com.sun.source.tree.ExpressionTree;
+import com.sun.source.tree.MethodInvocationTree;
+import com.sun.source.tree.MethodTree;
+import com.sun.source.tree.ModifiersTree;
+import com.sun.source.tree.StatementTree;
+import com.sun.source.tree.Tree;
+import com.sun.source.tree.Tree.Kind;
+import com.sun.source.tree.VariableTree;
+import com.sun.source.tree.WhileLoopTree;
+import com.sun.source.util.SourcePositions;
+import com.sun.source.util.TreeScanner;
+import com.sun.source.util.Trees;
+import com.sun.tools.javac.api.JavacTaskImpl;
+import com.sun.tools.javac.tree.JCTree;
+import java.io.IOException;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.DiagnosticListener;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.SimpleJavaFileObject;
+import javax.tools.ToolProvider;
+
+public class JavacParserTest extends TestCase {
+ final JavaCompiler tool;
+ public JavacParserTest(String testName) {
+ tool = ToolProvider.getSystemJavaCompiler();
+ System.out.println("java.home=" + System.getProperty("java.home"));
+ }
+
+ static class MyFileObject extends SimpleJavaFileObject {
+
+ private String text;
+
+ public MyFileObject(String text) {
+ super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
+ this.text = text;
+ }
+
+ @Override
+ public CharSequence getCharContent(boolean ignoreEncodingErrors) {
+ return text;
+ }
+ }
+
+ public void testPositionForSuperConstructorCalls() throws IOException {
+ assert tool != null;
+
+ String code = "package test; public class Test {public Test() {super();}}";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ SourcePositions pos = Trees.instance(ct).getSourcePositions();
+
+ MethodTree method =
+ (MethodTree) ((ClassTree) cut.getTypeDecls().get(0)).getMembers().get(0);
+ ExpressionStatementTree es =
+ (ExpressionStatementTree) method.getBody().getStatements().get(0);
+
+ assertEquals("testPositionForSuperConstructorCalls",
+ 72 - 24, pos.getStartPosition(cut, es));
+ assertEquals("testPositionForSuperConstructorCalls",
+ 80 - 24, pos.getEndPosition(cut, es));
+
+ MethodInvocationTree mit = (MethodInvocationTree) es.getExpression();
+
+ assertEquals("testPositionForSuperConstructorCalls",
+ 72 - 24, pos.getStartPosition(cut, mit));
+ assertEquals("testPositionForSuperConstructorCalls",
+ 79 - 24, pos.getEndPosition(cut, mit));
+
+ assertEquals("testPositionForSuperConstructorCalls",
+ 72 - 24, pos.getStartPosition(cut, mit.getMethodSelect()));
+ assertEquals("testPositionForSuperConstructorCalls",
+ 77 - 24, pos.getEndPosition(cut, mit.getMethodSelect()));
+
+ }
+
+ public void testPositionForEnumModifiers() throws IOException {
+
+ String code = "package test; public enum Test {A;}";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ SourcePositions pos = Trees.instance(ct).getSourcePositions();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ ModifiersTree mt = clazz.getModifiers();
+
+ assertEquals("testPositionForEnumModifiers",
+ 38 - 24, pos.getStartPosition(cut, mt));
+ assertEquals("testPositionForEnumModifiers",
+ 44 - 24, pos.getEndPosition(cut, mt));
+ }
+
+ public void testNewClassWithEnclosing() throws IOException {
+
+
+ String code = "package test; class Test { " +
+ "class d {} private void method() { " +
+ "Object o = Test.this.new d(); } }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ SourcePositions pos = Trees.instance(ct).getSourcePositions();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ ExpressionTree est =
+ ((VariableTree) ((MethodTree) clazz.getMembers().get(1)).getBody().getStatements().get(0)).getInitializer();
+
+ assertEquals("testNewClassWithEnclosing",
+ 97 - 24, pos.getStartPosition(cut, est));
+ assertEquals("testNewClassWithEnclosing",
+ 114 - 24, pos.getEndPosition(cut, est));
+ }
+
+ public void testPreferredPositionForBinaryOp() throws IOException {
+
+ String code = "package test; public class Test {" +
+ "private void test() {" +
+ "Object o = null; boolean b = o != null && o instanceof String;" +
+ "} private Test() {}}";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ MethodTree method = (MethodTree) clazz.getMembers().get(0);
+ VariableTree condSt = (VariableTree) method.getBody().getStatements().get(1);
+ BinaryTree cond = (BinaryTree) condSt.getInitializer();
+
+ JCTree condJC = (JCTree) cond;
+
+ assertEquals("testNewClassWithEnclosing",
+ 117 - 24, condJC.pos);
+ }
+
+ public void testPositionBrokenSource126732a() throws IOException {
+ String[] commands = new String[]{
+ "return Runnable()",
+ "do { } while (true)",
+ "throw UnsupportedOperationException()",
+ "assert true",
+ "1 + 1",};
+
+ for (String command : commands) {
+
+ String code = "package test;\n"
+ + "public class Test {\n"
+ + " public static void test() {\n"
+ + " " + command + " {\n"
+ + " new Runnable() {\n"
+ + " };\n"
+ + " }\n"
+ + "}";
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null,
+ null, null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ MethodTree method = (MethodTree) clazz.getMembers().get(0);
+ List<? extends StatementTree> statements =
+ method.getBody().getStatements();
+
+ StatementTree ret = statements.get(0);
+ StatementTree block = statements.get(1);
+
+ Trees t = Trees.instance(ct);
+ int len = code.indexOf(command + " {") + (command + " ").length();
+ assertEquals(command, len,
+ t.getSourcePositions().getEndPosition(cut, ret));
+ assertEquals(command, len,
+ t.getSourcePositions().getStartPosition(cut, block));
+ }
+ }
+
+ public void testPositionBrokenSource126732b() throws IOException {
+ String[] commands = new String[]{
+ "break",
+ "break A",
+ "continue ",
+ "continue A",};
+
+ for (String command : commands) {
+
+ String code = "package test;\n"
+ + "public class Test {\n"
+ + " public static void test() {\n"
+ + " while (true) {\n"
+ + " " + command + " {\n"
+ + " new Runnable() {\n"
+ + " };\n"
+ + " }\n"
+ + " }\n"
+ + "}";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null,
+ null, null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ MethodTree method = (MethodTree) clazz.getMembers().get(0);
+ List<? extends StatementTree> statements =
+ ((BlockTree) ((WhileLoopTree) method.getBody().getStatements().get(0)).getStatement()).getStatements();
+
+ StatementTree ret = statements.get(0);
+ StatementTree block = statements.get(1);
+
+ Trees t = Trees.instance(ct);
+ int len = code.indexOf(command + " {") + (command + " ").length();
+ assertEquals(command, len,
+ t.getSourcePositions().getEndPosition(cut, ret));
+ assertEquals(command, len,
+ t.getSourcePositions().getStartPosition(cut, block));
+ }
+ }
+
+ public void testErrorRecoveryForEnhancedForLoop142381() throws IOException {
+
+ String code = "package test; class Test { " +
+ "private void method() { " +
+ "java.util.Set<String> s = null; for (a : s) {} } }";
+
+ final List<Diagnostic<? extends JavaFileObject>> errors =
+ new LinkedList<Diagnostic<? extends JavaFileObject>>();
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null,
+ new DiagnosticListener<JavaFileObject>() {
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ errors.add(diagnostic);
+ }
+ }, null, null, Arrays.asList(new MyFileObject(code)));
+
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ StatementTree forStatement =
+ ((MethodTree) clazz.getMembers().get(0)).getBody().getStatements().get(1);
+
+ assertEquals("testErrorRecoveryForEnhancedForLoop142381",
+ Kind.ENHANCED_FOR_LOOP, forStatement.getKind());
+ assertFalse("testErrorRecoveryForEnhancedForLoop142381", errors.isEmpty());
+ }
+
+ public void testPositionAnnotationNoPackage187551() throws IOException {
+
+ String code = "\n@interface Test {}";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ Trees t = Trees.instance(ct);
+
+ assertEquals("testPositionAnnotationNoPackage187551",
+ 1, t.getSourcePositions().getStartPosition(cut, clazz));
+ }
+
+ public void testPositionsSane() throws IOException {
+ performPositionsSanityTest("package test; class Test { " +
+ "private void method() { " +
+ "java.util.List<? extends java.util.List<? extends String>> l; " +
+ "} }");
+ performPositionsSanityTest("package test; class Test { " +
+ "private void method() { " +
+ "java.util.List<? super java.util.List<? super String>> l; " +
+ "} }");
+ performPositionsSanityTest("package test; class Test { " +
+ "private void method() { " +
+ "java.util.List<? super java.util.List<?>> l; } }");
+ }
+
+ private void performPositionsSanityTest(String code) throws IOException {
+
+ final List<Diagnostic<? extends JavaFileObject>> errors =
+ new LinkedList<Diagnostic<? extends JavaFileObject>>();
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null,
+ new DiagnosticListener<JavaFileObject>() {
+
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ errors.add(diagnostic);
+ }
+ }, null, null, Arrays.asList(new MyFileObject(code)));
+
+ final CompilationUnitTree cut = ct.parse().iterator().next();
+ final Trees trees = Trees.instance(ct);
+
+ new TreeScanner<Void, Void>() {
+
+ private long parentStart = 0;
+ private long parentEnd = Integer.MAX_VALUE;
+
+ @Override
+ public Void scan(Tree node, Void p) {
+ if (node == null) {
+ return null;
+ }
+
+ long start = trees.getSourcePositions().getStartPosition(cut, node);
+
+ if (start == (-1)) {
+ return null; //synthetic tree
+ }
+ assertTrue(node.toString() + ":" + start + "/" + parentStart,
+ parentStart <= start);
+
+ long prevParentStart = parentStart;
+
+ parentStart = start;
+
+ long end = trees.getSourcePositions().getEndPosition(cut, node);
+
+ assertTrue(node.toString() + ":" + end + "/" + parentEnd,
+ end <= parentEnd);
+
+ long prevParentEnd = parentEnd;
+
+ parentEnd = end;
+
+ super.scan(node, p);
+
+ parentStart = prevParentStart;
+ parentEnd = prevParentEnd;
+
+ return null;
+ }
+
+ private void assertTrue(String message, boolean b) {
+ if (!b) fail(message);
+ }
+ }.scan(cut, null);
+ }
+
+ public void testCorrectWilcardPositions() throws IOException {
+ performWildcardPositionsTest("package test; import java.util.List; " +
+ "class Test { private void method() { List<? extends List<? extends String>> l; } }",
+
+ Arrays.asList("List<? extends List<? extends String>> l;",
+ "List<? extends List<? extends String>>",
+ "List",
+ "? extends List<? extends String>",
+ "List<? extends String>",
+ "List",
+ "? extends String",
+ "String"));
+ performWildcardPositionsTest("package test; import java.util.List; " +
+ "class Test { private void method() { List<? super List<? super String>> l; } }",
+
+ Arrays.asList("List<? super List<? super String>> l;",
+ "List<? super List<? super String>>",
+ "List",
+ "? super List<? super String>",
+ "List<? super String>",
+ "List",
+ "? super String",
+ "String"));
+ performWildcardPositionsTest("package test; import java.util.List; " +
+ "class Test { private void method() { List<? super List<?>> l; } }",
+
+ Arrays.asList("List<? super List<?>> l;",
+ "List<? super List<?>>",
+ "List",
+ "? super List<?>",
+ "List<?>",
+ "List",
+ "?"));
+ performWildcardPositionsTest("package test; import java.util.List; " +
+ "class Test { private void method() { " +
+ "List<? extends List<? extends List<? extends String>>> l; } }",
+
+ Arrays.asList("List<? extends List<? extends List<? extends String>>> l;",
+ "List<? extends List<? extends List<? extends String>>>",
+ "List",
+ "? extends List<? extends List<? extends String>>",
+ "List<? extends List<? extends String>>",
+ "List",
+ "? extends List<? extends String>",
+ "List<? extends String>",
+ "List",
+ "? extends String",
+ "String"));
+ performWildcardPositionsTest("package test; import java.util.List; " +
+ "class Test { private void method() { " +
+ "List<? extends List<? extends List<? extends String >>> l; } }",
+ Arrays.asList("List<? extends List<? extends List<? extends String >>> l;",
+ "List<? extends List<? extends List<? extends String >>>",
+ "List",
+ "? extends List<? extends List<? extends String >>",
+ "List<? extends List<? extends String >>",
+ "List",
+ "? extends List<? extends String >",
+ "List<? extends String >",
+ "List",
+ "? extends String",
+ "String"));
+ }
+
+ public void performWildcardPositionsTest(final String code,
+ List<String> golden) throws IOException {
+
+ final List<Diagnostic<? extends JavaFileObject>> errors =
+ new LinkedList<Diagnostic<? extends JavaFileObject>>();
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null,
+ new DiagnosticListener<JavaFileObject>() {
+ public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
+ errors.add(diagnostic);
+ }
+ }, null, null, Arrays.asList(new MyFileObject(code)));
+
+ final CompilationUnitTree cut = ct.parse().iterator().next();
+ final List<String> content = new LinkedList<String>();
+ final Trees trees = Trees.instance(ct);
+
+ new TreeScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree node, Void p) {
+ if (node == null) {
+ return null;
+ }
+ long start = trees.getSourcePositions().getStartPosition(cut, node);
+
+ if (start == (-1)) {
+ return null; //synthetic tree
+ }
+ long end = trees.getSourcePositions().getEndPosition(cut, node);
+ String s = code.substring((int) start, (int) end);
+ content.add(s);
+
+ return super.scan(node, p);
+ }
+ }.scan(((MethodTree) ((ClassTree) cut.getTypeDecls().get(0)).getMembers().get(0)).getBody().getStatements().get(0), null);
+
+ assertEquals("performWildcardPositionsTest",golden.toString(),
+ content.toString());
+ }
+
+ public void testStartPositionForMethodWithoutModifiers() throws IOException {
+
+ String code = "package t; class Test { <T> void t() {} }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ MethodTree mt = (MethodTree) clazz.getMembers().get(0);
+ Trees t = Trees.instance(ct);
+ int start = (int) t.getSourcePositions().getStartPosition(cut, mt);
+ int end = (int) t.getSourcePositions().getEndPosition(cut, mt);
+
+ assertEquals("testStartPositionForMethodWithoutModifiers",
+ "<T> void t() {}", code.substring(start, end));
+ }
+
+ public void testStartPositionEnumConstantInit() throws IOException {
+
+ String code = "package t; enum Test { AAA; }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+ ClassTree clazz = (ClassTree) cut.getTypeDecls().get(0);
+ VariableTree enumAAA = (VariableTree) clazz.getMembers().get(0);
+ Trees t = Trees.instance(ct);
+ int start = (int) t.getSourcePositions().getStartPosition(cut,
+ enumAAA.getInitializer());
+
+ assertEquals("testStartPositionEnumConstantInit", -1, start);
+ }
+
+ public void testVariableInIfThen1() throws IOException {
+
+ String code = "package t; class Test { " +
+ "private static void t(String name) { " +
+ "if (name != null) String nn = name.trim(); } }";
+
+ DiagnosticCollector<JavaFileObject> coll =
+ new DiagnosticCollector<JavaFileObject>();
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null,
+ null, Arrays.asList(new MyFileObject(code)));
+
+ ct.parse();
+
+ List<String> codes = new LinkedList<String>();
+
+ for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
+ codes.add(d.getCode());
+ }
+
+ assertEquals("testVariableInIfThen1",
+ Arrays.<String>asList("compiler.err.variable.not.allowed"),
+ codes);
+ }
+
+ public void testVariableInIfThen2() throws IOException {
+
+ String code = "package t; class Test { " +
+ "private static void t(String name) { " +
+ "if (name != null) class X {} } }";
+ DiagnosticCollector<JavaFileObject> coll =
+ new DiagnosticCollector<JavaFileObject>();
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null,
+ null, Arrays.asList(new MyFileObject(code)));
+
+ ct.parse();
+
+ List<String> codes = new LinkedList<String>();
+
+ for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
+ codes.add(d.getCode());
+ }
+
+ assertEquals("testVariableInIfThen2",
+ Arrays.<String>asList("compiler.err.class.not.allowed"), codes);
+ }
+
+ public void testVariableInIfThen3() throws IOException {
+
+ String code = "package t; class Test { "+
+ "private static void t(String name) { " +
+ "if (name != null) abstract } }";
+ DiagnosticCollector<JavaFileObject> coll =
+ new DiagnosticCollector<JavaFileObject>();
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null,
+ null, Arrays.asList(new MyFileObject(code)));
+
+ ct.parse();
+
+ List<String> codes = new LinkedList<String>();
+
+ for (Diagnostic<? extends JavaFileObject> d : coll.getDiagnostics()) {
+ codes.add(d.getCode());
+ }
+
+ assertEquals("testVariableInIfThen3",
+ Arrays.<String>asList("compiler.err.illegal.start.of.expr"),
+ codes);
+ }
+
+ //see javac bug #6882235, NB bug #98234:
+ public void testMissingExponent() throws IOException {
+
+ String code = "\nclass Test { { System.err.println(0e); } }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+
+ assertNotNull(ct.parse().iterator().next());
+ }
+
+ public void testTryResourcePos() throws IOException {
+
+ final String code = "package t; class Test { " +
+ "{ try (java.io.InputStream in = null) { } } }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ new TreeScanner<Void, Void>() {
+ @Override
+ public Void visitVariable(VariableTree node, Void p) {
+ if ("in".contentEquals(node.getName())) {
+ JCTree.JCVariableDecl var = (JCTree.JCVariableDecl) node;
+ System.out.println(node.getName() + "," + var.pos);
+ assertEquals("testTryResourcePos", "in = null) { } } }",
+ code.substring(var.pos));
+ }
+ return super.visitVariable(node, p);
+ }
+ }.scan(cut, null);
+ }
+
+ public void testVarPos() throws IOException {
+
+ final String code = "package t; class Test { " +
+ "{ java.io.InputStream in = null; } }";
+
+ JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, null, null,
+ null, Arrays.asList(new MyFileObject(code)));
+ CompilationUnitTree cut = ct.parse().iterator().next();
+
+ new TreeScanner<Void, Void>() {
+
+ @Override
+ public Void visitVariable(VariableTree node, Void p) {
+ if ("in".contentEquals(node.getName())) {
+ JCTree.JCVariableDecl var = (JCTree.JCVariableDecl) node;
+ assertEquals("testVarPos","in = null; } }",
+ code.substring(var.pos));
+ }
+ return super.visitVariable(node, p);
+ }
+ }.scan(cut, null);
+ }
+
+ void testsNotWorking() throws IOException {
+
+ // Fails with nb-javac, needs further investigation
+ testPositionBrokenSource126732a();
+ testPositionBrokenSource126732b();
+
+ // Fails, these tests yet to be addressed
+ testVariableInIfThen1();
+ testVariableInIfThen2();
+ testPositionForEnumModifiers();
+ testStartPositionEnumConstantInit();
+ }
+ void testPositions() throws IOException {
+ testPositionsSane();
+ testCorrectWilcardPositions();
+ testPositionAnnotationNoPackage187551();
+ testPositionForSuperConstructorCalls();
+ testPreferredPositionForBinaryOp();
+ testStartPositionForMethodWithoutModifiers();
+ testVarPos();
+ testVariableInIfThen3();
+ testTryResourcePos();
+ }
+
+ public static void main(String... args) throws IOException {
+ JavacParserTest jpt = new JavacParserTest("JavacParserTest");
+ jpt.testPositions();
+ System.out.println("PASS");
+ }
+}
+
+abstract class TestCase {
+
+ void assertEquals(String message, int i, int pos) {
+ if (i != pos) {
+ fail(message);
+ }
+ }
+
+ void assertFalse(String message, boolean empty) {
+ throw new UnsupportedOperationException("Not yet implemented");
+ }
+
+ void assertEquals(String message, int i, long l) {
+ if (i != l) {
+ fail(message + ":" + i + ":" + l);
+ }
+ }
+
+ void assertEquals(String message, Object o1, Object o2) {
+ System.out.println(o1);
+ System.out.println(o2);
+ if (o1 != null && o2 != null && !o1.equals(o2)) {
+ fail(message);
+ }
+ if (o1 == null && o2 != null) {
+ fail(message);
+ }
+ }
+
+ void assertNotNull(Object o) {
+ if (o == null) {
+ fail();
+ }
+ }
+
+ void fail() {
+ fail("test failed");
+ }
+
+ void fail(String message) {
+ throw new RuntimeException(message);
+ }
+}