# HG changeset patch # User ksrini # Date 1314893665 25200 # Node ID 2f776c884f48dcb9d685f1f6baaa789f314b6793 # Parent 8c9f07f9aa286a568da576dc70400b2c1ad05edd 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 diff -r 8c9f07f9aa28 -r 2f776c884f48 src/share/classes/com/sun/tools/javac/parser/JavacParser.java --- a/src/share/classes/com/sun/tools/javac/parser/JavacParser.java Wed Jul 27 19:01:33 2011 +0100 +++ b/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.nil(), key, args); } private JCErroneous syntaxError(int pos, List 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 docComments; + private final Map 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.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.of(wc, id)); + JCErroneous err = F.at(pos).Erroneous(List.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.nil(), null)); return toP(F.at(newpos).Erroneous(List.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.nil(), null)); + return syntaxError(S.pos(), List.of(t), "array.dimension.missing"); } } else { ListBuffer dims = new ListBuffer(); @@ -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 annotations = new ListBuffer(); 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 classOrInterfaceBodyDeclaration(Name className, boolean isInterface) { + protected List classOrInterfaceBodyDeclaration(Name className, boolean isInterface) { if (S.token() == SEMI) { S.nextToken(); return List.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.of(t)); + JCExpression ret = F.at(t.pos).Erroneous(List.of(t)); + error(ret, "not.stmt"); + return ret; } } diff -r 8c9f07f9aa28 -r 2f776c884f48 src/share/classes/com/sun/tools/javac/parser/Scanner.java --- a/src/share/classes/com/sun/tools/javac/parser/Scanner.java Wed Jul 27 19:01:33 2011 +0100 +++ b/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>} 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; } diff -r 8c9f07f9aa28 -r 2f776c884f48 src/share/classes/com/sun/tools/javac/util/AbstractLog.java --- a/src/share/classes/com/sun/tools/javac/util/AbstractLog.java Wed Jul 27 19:01:33 2011 +0100 +++ b/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. diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/BadTwr.out --- a/test/tools/javac/TryWithResources/BadTwr.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/DuplicateResourceDecl.out --- a/test/tools/javac/TryWithResources/DuplicateResourceDecl.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/ResourceInterface.out --- a/test/tools/javac/TryWithResources/ResourceInterface.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/TwrFlow.out --- a/test/tools/javac/TryWithResources/TwrFlow.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/TwrLint.out --- a/test/tools/javac/TryWithResources/TwrLint.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/TryWithResources/TwrOnNonResource.out --- a/test/tools/javac/TryWithResources/TwrOnNonResource.out Wed Jul 27 19:01:33 2011 +0100 +++ b/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 diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/diags/examples/EmptyCharLiteral.java --- a/test/tools/javac/diags/examples/EmptyCharLiteral.java Wed Jul 27 19:01:33 2011 +0100 +++ b/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 { diff -r 8c9f07f9aa28 -r 2f776c884f48 test/tools/javac/parser/netbeans/JavacParserTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/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 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 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 s = null; for (a : s) {} } }"; + + final List> errors = + new LinkedList>(); + + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, + new DiagnosticListener() { + public void report(Diagnostic 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> l; " + + "} }"); + performPositionsSanityTest("package test; class Test { " + + "private void method() { " + + "java.util.List> l; " + + "} }"); + performPositionsSanityTest("package test; class Test { " + + "private void method() { " + + "java.util.List> l; } }"); + } + + private void performPositionsSanityTest(String code) throws IOException { + + final List> errors = + new LinkedList>(); + + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, + new DiagnosticListener() { + + public void report(Diagnostic 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() { + + 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> l; } }", + + Arrays.asList("List> l;", + "List>", + "List", + "? extends List", + "List", + "List", + "? extends String", + "String")); + performWildcardPositionsTest("package test; import java.util.List; " + + "class Test { private void method() { List> l; } }", + + Arrays.asList("List> l;", + "List>", + "List", + "? super List", + "List", + "List", + "? super String", + "String")); + performWildcardPositionsTest("package test; import java.util.List; " + + "class Test { private void method() { List> l; } }", + + Arrays.asList("List> l;", + "List>", + "List", + "? super List", + "List", + "List", + "?")); + performWildcardPositionsTest("package test; import java.util.List; " + + "class Test { private void method() { " + + "List>> l; } }", + + Arrays.asList("List>> l;", + "List>>", + "List", + "? extends List>", + "List>", + "List", + "? extends List", + "List", + "List", + "? extends String", + "String")); + performWildcardPositionsTest("package test; import java.util.List; " + + "class Test { private void method() { " + + "List>> l; } }", + Arrays.asList("List>> l;", + "List>>", + "List", + "? extends List>", + "List>", + "List", + "? extends List", + "List", + "List", + "? extends String", + "String")); + } + + public void performWildcardPositionsTest(final String code, + List golden) throws IOException { + + final List> errors = + new LinkedList>(); + + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, + new DiagnosticListener() { + public void report(Diagnostic diagnostic) { + errors.add(diagnostic); + } + }, null, null, Arrays.asList(new MyFileObject(code))); + + final CompilationUnitTree cut = ct.parse().iterator().next(); + final List content = new LinkedList(); + final Trees trees = Trees.instance(ct); + + new TreeScanner() { + @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 { 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", + " 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 coll = + new DiagnosticCollector(); + + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null, + null, Arrays.asList(new MyFileObject(code))); + + ct.parse(); + + List codes = new LinkedList(); + + for (Diagnostic d : coll.getDiagnostics()) { + codes.add(d.getCode()); + } + + assertEquals("testVariableInIfThen1", + Arrays.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 coll = + new DiagnosticCollector(); + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null, + null, Arrays.asList(new MyFileObject(code))); + + ct.parse(); + + List codes = new LinkedList(); + + for (Diagnostic d : coll.getDiagnostics()) { + codes.add(d.getCode()); + } + + assertEquals("testVariableInIfThen2", + Arrays.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 coll = + new DiagnosticCollector(); + JavacTaskImpl ct = (JavacTaskImpl) tool.getTask(null, null, coll, null, + null, Arrays.asList(new MyFileObject(code))); + + ct.parse(); + + List codes = new LinkedList(); + + for (Diagnostic d : coll.getDiagnostics()) { + codes.add(d.getCode()); + } + + assertEquals("testVariableInIfThen3", + Arrays.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() { + @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() { + + @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); + } +}